home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume23 / trn / part08 < prev    next >
Encoding:
Text File  |  1991-08-22  |  64.3 KB  |  2,684 lines

  1. This is a new archive version of TRN at patchlevel 3.
  2. The original posting took up Volume23, issues 60 to 73, with
  3. various problems.  These files replace those issues.
  4.  
  5. #! /bin/sh
  6. # This is a shell archive.  Remove anything before this line, then feed it
  7. # into a shell via "sh file" or similar.  To overwrite existing files,
  8. # type "sh file -c".
  9. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  10. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  11. # Contents:  respond.c rt-rn.c rt-select.c uudecode.h
  12. # Wrapped by rsalz@litchi.bbn.com on Fri Aug 23 16:38:57 1991
  13. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  14. echo If this archive is complete, you will see the following message:
  15. echo '          "shar: End of archive 8 (of 14)."'
  16. if test -f 'respond.c' -a "${1}" != "-c" ; then 
  17.   echo shar: Will not clobber existing file \"'respond.c'\"
  18. else
  19.   echo shar: Extracting \"'respond.c'\" \(16294 characters\)
  20.   sed "s/^X//" >'respond.c' <<'END_OF_FILE'
  21. X/* $Header: respond.c,v 4.3.3.2 91/01/16 03:28:40 davison Trn $
  22. X *
  23. X * $Log:    respond.c,v $
  24. X * Revision 4.3.3.2  91/01/16  03:28:40  davison
  25. X * Integrated rn patches 48-54.  Changed fseek to allow for NL_SIZE.
  26. X * 
  27. X * Revision 4.3.3.1  90/07/21  20:30:18  davison
  28. X * Initial Trn Release
  29. X * 
  30. X * Revision 4.3.2.4  90/11/22  16:09:04  sob
  31. X * Added changes to accomodate pickly C preprocessors
  32. X * 
  33. X * Revision 4.3.2.3  90/03/22  23:05:19  sob
  34. X * Fixes provided by Wayne Davison <drivax!davison>
  35. X * 
  36. X * Revision 4.3.2.2  89/11/26  18:25:10  sob
  37. X * Enlarged the size of the header buffer to accomodate long references lines.
  38. X * Fix provided by Joe Buck.
  39. X * 
  40. X * Revision 4.3.2.1  89/11/06  01:00:26  sob
  41. X * Added RRN support from NNTP 1.5
  42. X * 
  43. X * Revision 4.3.1.5  85/09/10  11:05:00  lwall
  44. X * Improved %m in in_char().
  45. X * 
  46. X * Revision 4.3.1.4  85/05/23  17:24:49  lwall
  47. X * Now allows 'r' and 'f' on null articles.
  48. X * 
  49. X * Revision 4.3.1.3  85/05/15  14:42:32  lwall
  50. X * Removed duplicate include of intrp.h.
  51. X * 
  52. X * Revision 4.3.1.2  85/05/14  08:55:15  lwall
  53. X * Default for normal/mailbox question was applied to wrong buffer.
  54. X * 
  55. X * Revision 4.3.1.1  85/05/10  11:37:33  lwall
  56. X * Branch for patches.
  57. X * 
  58. X * Revision 4.3  85/05/01  11:47:04  lwall
  59. X * Baseline for release with 4.3bsd.
  60. X * 
  61. X */
  62. X
  63. X#include "EXTERN.h"
  64. X#include "common.h"
  65. X#include "intrp.h"
  66. X#include "head.h"
  67. X#include "term.h"
  68. X#include "ng.h"
  69. X#include "util.h"
  70. X#include "rn.h"
  71. X#include "artio.h"
  72. X#include "final.h"
  73. X#include "uudecode.h"
  74. X#include "INTERN.h"
  75. X#include "respond.h"
  76. X
  77. Xstatic char nullart[] = "\nNull article\n";
  78. X
  79. Xvoid
  80. Xrespond_init()
  81. X{
  82. X    ;
  83. X}
  84. X
  85. Xint
  86. Xsave_article()
  87. X{
  88. X    bool use_pref, cut_line();
  89. X    register char *s, *c;
  90. X    char altbuf[CBUFLEN];
  91. X    int iter;
  92. X    bool interactive = (buf[1] == FINISHCMD);
  93. X    char cmd = *buf;
  94. X    
  95. X    if (!finish_command(interactive))    /* get rest of command */
  96. X    return SAVE_ABORT;
  97. X    if ((use_pref = isupper(cmd)) != 0)
  98. X    cmd = tolower(cmd);
  99. X#ifdef ASYNC_PARSE
  100. X    parse_maybe(art);
  101. X#endif
  102. X    savefrom = (cmd == 'w' || cmd == 'e' ? htype[PAST_HEADER].ht_minpos : 0);
  103. X    if (artopen(art) == Nullfp) {
  104. X#ifdef VERBOSE
  105. X    IF(verbose)
  106. X        fputs("\n\
  107. XSaving null articles is not very productive!  :-)\n\
  108. X",stdout) FLUSH;
  109. X    ELSE
  110. X#endif
  111. X#ifdef TERSE
  112. X        fputs(nullart,stdout) FLUSH;
  113. X#endif
  114. X    return SAVE_DONE;
  115. X    }
  116. X    if (chdir(cwd)) {
  117. X    printf(nocd,cwd) FLUSH;
  118. X    sig_catcher(0);
  119. X    }
  120. X    if (cmd == 'e') {        /* is this an extract command? */
  121. X    int cnt = 0;
  122. X    bool found_cut = FALSE;
  123. X    char art_buf[LBUFLEN], *cmdstr;
  124. X
  125. X    s = buf+1;        /* skip e */
  126. X    while (*s == ' ') s++;    /* skip leading spaces */
  127. X    safecpy(altbuf,filexp(s),sizeof altbuf);
  128. X    s = altbuf;
  129. X    if (extractprog) {
  130. X        free(extractprog);
  131. X        extractprog = Nullch;
  132. X    }
  133. X    if (*s) {
  134. X        cmdstr = cpytill(buf,s,'|');    /* check for | */
  135. X        s = buf + strlen(buf)-1;
  136. X        while (*s == ' ') s--;        /* trim trailing spaces */
  137. X        *++s = '\0';
  138. X        if (*cmdstr) {
  139. X        s = cmdstr+1;            /* skip | */
  140. X        while (*s == ' ') s++;
  141. X        if (strEQ(s,"-"))
  142. X            cmdstr = Nullch;
  143. X        else {
  144. X            extractprog = savestr(s);    /* put extracter in %e */
  145. X            if (uu_out != Nullfp)
  146. X            uud_end();
  147. X        }
  148. X        } else
  149. X        cmdstr = Nullch;
  150. X        s = buf;
  151. X    } else
  152. X        cmdstr = Nullch;
  153. X
  154. X    fseek(artfp,savefrom,0);
  155. X    if ((cmd = *s) == '\0')
  156. X        interp(s = buf, (sizeof buf), getval("SAVEDIR",SAVEDIR));
  157. X    if (*s != '/') {        /* relative path? */
  158. X        c = (s==buf ? altbuf : buf);
  159. X        sprintf(c, "%s/%s", cwd, s);
  160. X        s = c;            /* absolutize it */
  161. X    }
  162. X    if (uu_out != Nullfp) {
  163. X        printf("Continuing %s:%s\n", uu_fname,
  164. X        cmd != '\0' && strNE(savedest,s) ?
  165. X         " (Ignoring conflicting directory)" : nullstr );
  166. X        uudecode(artfp);
  167. X    }
  168. X    else {
  169. X        if (savedest)
  170. X        free(savedest);
  171. X        s = savedest = savestr(s);    /* make it handy for %b */
  172. X        if (makedir(s, MD_DIR)) {    /* ensure directory exists */
  173. X        int_count++;
  174. X        return SAVE_DONE;
  175. X        }
  176. X        if (chdir(s)) {
  177. X        printf(nocd,s) FLUSH;
  178. X        sig_catcher(0);
  179. X        }
  180. X        s = getwd(buf);        /* simplify path for output */
  181. X        while(fgets(art_buf,LBUFLEN,artfp) != Nullch) {
  182. X        if (*art_buf <= ' ')
  183. X            continue;    /* Ignore empty or initially-whitespace lines */
  184. X        if (found_cut && cmdstr) {
  185. X            printf("Extracting data into %s using %s:\n",
  186. X            s, extractprog);
  187. X            goto extract_it;
  188. X        }
  189. X        if (((*art_buf == '#' || *art_buf == ':')
  190. X          && (strnEQ(art_buf+1, "! /bin/sh", 9)
  191. X           || strnEQ(art_buf+1, "!/bin/sh", 8)
  192. X           || strnEQ(art_buf+2, "This is ", 8)))
  193. X         || strnEQ(art_buf, "sed ", 4)
  194. X         || strnEQ(art_buf, "cat ", 4)
  195. X         || strnEQ(art_buf, "echo ", 5)) {
  196. X            fseek(artfp,(long)-strlen(art_buf)-NL_SIZE+1,1);
  197. X            savefrom = ftell(artfp);
  198. X            if (cmdstr) {
  199. X            printf("Extracting shar into %s using %s:\n",
  200. X                s, extractprog);
  201. X            goto extract_it;
  202. X            }
  203. X            /* Check for special-case of shar'ed-uuencoded file */
  204. X            while(fgets(art_buf,LBUFLEN,artfp) != Nullch) {
  205. X            if (*art_buf == '#' || *art_buf == ':'
  206. X             || strnEQ(art_buf, "echo ", 5)
  207. X             || strnEQ(art_buf, "sed ", 4))
  208. X                continue;
  209. X            if (strnEQ(art_buf, "Xbegin ", 7))
  210. X                goto uu_decode;
  211. X            break;
  212. X            }
  213. X            printf("Extracting shar into %s:\n", s);
  214. X            extractprog = savestr(filexp(getval("UNSHAR",UNSHAR)));
  215. X          extract_it:
  216. X            cnt = 0;
  217. X            interp(cmd_buf,(sizeof cmd_buf),getval("EXSAVER",EXSAVER));
  218. X            resetty();        /* restore tty state */
  219. X            doshell(SH,cmd_buf);
  220. X            noecho();        /* revert to cbreaking */
  221. X            crmode();
  222. X            break;
  223. X        }
  224. X        else
  225. X        if (!cmdstr
  226. X         && (strnEQ(art_buf,"table ", 6)
  227. X          || strnEQ(art_buf,"table\n", 6)
  228. X          || strnEQ(art_buf,"begin ", 6))) {
  229. X         uu_decode:
  230. X            printf("Extracting uuencoded file into %s:\n", s);
  231. X            extractprog = savestr("-");
  232. X            cnt = 0;
  233. X            fseek(artfp,(long)-strlen(art_buf)-NL_SIZE+1,1);
  234. X            savefrom = ftell(artfp);
  235. X            uud_start(s);
  236. X            uudecode(artfp);
  237. X            break;
  238. X        }
  239. X        else {
  240. X            if (cut_line(art_buf)) {
  241. X            savefrom = ftell(artfp);
  242. X            found_cut = TRUE;
  243. X            }
  244. X            else if (found_cut || ++cnt == 200) {
  245. X            break;
  246. X            }
  247. X        }
  248. X        }/* while */
  249. X        if (cnt) {
  250. X        if (!cmdstr)
  251. X            extractprog = savestr("-");
  252. X        printf("Unable to determine type of file.\n");
  253. X        }
  254. X    }/* if */
  255. X    }
  256. X    else if ((s = index(buf,'|')) != Nullch) {
  257. X                /* is it a pipe command? */
  258. X    s++;            /* skip the | */
  259. X    while (*s == ' ') s++;
  260. X    safecpy(altbuf,filexp(s),sizeof altbuf);
  261. X    if (savedest)
  262. X        free(savedest);
  263. X    savedest = savestr(altbuf);
  264. X    interp(cmd_buf, (sizeof cmd_buf), getval("PIPESAVER",PIPESAVER));
  265. X                /* then set up for command */
  266. X    resetty();        /* restore tty state */
  267. X    if (use_pref)        /* use preferred shell? */
  268. X        doshell(Nullch,cmd_buf);
  269. X                /* do command with it */
  270. X    else
  271. X        doshell(sh,cmd_buf);    /* do command with sh */
  272. X    noecho();        /* and stop echoing */
  273. X    crmode();        /* and start cbreaking */
  274. X    }
  275. X    else {            /* normal save */
  276. X    bool there, mailbox;
  277. X    char *savename = getval("SAVENAME",SAVENAME);
  278. X
  279. X    s = buf+1;        /* skip s or S */
  280. X    if (*s == '-') {    /* if they are confused, skip - also */
  281. X#ifdef VERBOSE
  282. X        IF(verbose)
  283. X        fputs("Warning: '-' ignored.  This isn't readnews.\n",stdout)
  284. X          FLUSH;
  285. X        ELSE
  286. X#endif
  287. X#ifdef TERSE
  288. X        fputs("'-' ignored.\n",stdout) FLUSH;
  289. X#endif
  290. X        s++;
  291. X    }
  292. X    for (; *s == ' '; s++);    /* skip spaces */
  293. X    safecpy(altbuf,filexp(s),sizeof altbuf);
  294. X    s = altbuf;
  295. X    if (! index(s,'/')) {
  296. X        interp(buf, (sizeof buf), getval("SAVEDIR",SAVEDIR));
  297. X        if (makedir(buf,MD_DIR))    /* ensure directory exists */
  298. X        strcpy(buf,cwd);
  299. X        if (*s) {
  300. X        for (c = buf; *c; c++) ;
  301. X        *c++ = '/';
  302. X        strcpy(c,s);        /* add filename */
  303. X        }
  304. X        s = buf;
  305. X    }
  306. X    for (iter = 0;
  307. X        (there = stat(s,&filestat) >= 0) &&
  308. X        (filestat.st_mode & S_IFDIR);
  309. X        iter++) {            /* is it a directory? */
  310. X
  311. X        c = (s+strlen(s));
  312. X        *c++ = '/';            /* put a slash before filename */
  313. X        interp(c, s==buf?(sizeof buf):(sizeof altbuf),
  314. X        iter ? "News" : savename );
  315. X                /* generate a default name somehow or other */
  316. X        if (index(c,'/')) {        /* yikes, a '/' in the filename */
  317. X        makedir(s,MD_FILE);
  318. X        }
  319. X    }
  320. X    if (*s != '/') {        /* relative path? */
  321. X        c = (s==buf ? altbuf : buf);
  322. X        sprintf(c, "%s/%s", cwd, s);
  323. X        s = c;            /* absolutize it */
  324. X    }
  325. X    if (savedest)
  326. X        free(savedest);
  327. X    s = savedest = savestr(s);    /* doesn't move any more */
  328. X                    /* make it handy for %b */
  329. X    if (!there) {
  330. X        if (mbox_always)
  331. X        mailbox = TRUE;
  332. X        else if (norm_always)
  333. X        mailbox = FALSE;
  334. X        else {
  335. X        char *dflt = (instr(savename,"%a") ? "nyq" : "ynq");
  336. X        
  337. X        sprintf(cmd_buf,
  338. X        "\nFile %s doesn't exist--\n    use mailbox format? [%s] ",
  339. X          s,dflt);
  340. X          reask_save:
  341. X        in_char(cmd_buf, 'M');
  342. X        putchar('\n') FLUSH;
  343. X        setdef(buf,dflt);
  344. X#ifdef VERIFY
  345. X        printcmd();
  346. X#endif
  347. X        if (*buf == 'h') {
  348. X#ifdef VERBOSE
  349. X            IF(verbose)
  350. X            printf("\n\
  351. XType y to create %s as a mailbox.\n\
  352. XType n to create it as a normal file.\n\
  353. XType q to abort the save.\n\
  354. X",s) FLUSH;
  355. X            ELSE
  356. X#endif
  357. X#ifdef TERSE
  358. X            fputs("\n\
  359. Xy to create mailbox.\n\
  360. Xn to create normal file.\n\
  361. Xq to abort.\n\
  362. X",stdout) FLUSH;
  363. X#endif
  364. X            goto reask_save;
  365. X        }
  366. X        else if (*buf == 'n') {
  367. X            mailbox = FALSE;
  368. X        }
  369. X        else if (*buf == 'y') {
  370. X            mailbox = TRUE;
  371. X        }
  372. X        else if (*buf == 'q') {
  373. X            goto s_bomb;
  374. X        }
  375. X        else {
  376. X            fputs(hforhelp,stdout) FLUSH;
  377. X            settle_down();
  378. X            goto reask_save;
  379. X        }
  380. X        }
  381. X    }
  382. X    else if (filestat.st_mode & S_IFCHR)
  383. X        mailbox = FALSE;
  384. X    else {
  385. X        int tmpfd;
  386. X        
  387. X        tmpfd = open(s,0);
  388. X        if (tmpfd == -1)
  389. X        mailbox = FALSE;
  390. X        else {
  391. X        read(tmpfd,buf,LBUFLEN);
  392. X        c = buf;
  393. X        if (!isspace(MBOXCHAR))
  394. X            while (isspace(*c))
  395. X            c++;
  396. X        mailbox = (*c == MBOXCHAR);
  397. X        close(tmpfd);
  398. X        }
  399. X    }
  400. X
  401. X    safecpy(cmd_buf, filexp(mailbox ?
  402. X        getval("MBOXSAVER",MBOXSAVER) :
  403. X        getval("NORMSAVER",NORMSAVER) ), sizeof cmd_buf);
  404. X                /* format the command */
  405. X    resetty();        /* make terminal behave */
  406. X    if (doshell(use_pref?Nullch:SH,cmd_buf))
  407. X        fputs("Not saved",stdout);
  408. X    else
  409. X        printf("%s to %s %s",
  410. X          there?"Appended":"Saved",
  411. X          mailbox?"mailbox":"file",
  412. X          s);
  413. X    if (interactive)
  414. X        putchar('\n') FLUSH;
  415. X    noecho();        /* make terminal do what we want */
  416. X    crmode();
  417. X    }
  418. Xs_bomb:
  419. X#ifdef SERVER
  420. X    if (chdir(spool)) {
  421. X#else /* not SERVER */
  422. X    if (chdir(spool) || chdir(ngdir)) {
  423. X#endif /* SERVER */
  424. X    printf(nocd,ngdir) FLUSH;
  425. X    sig_catcher(0);
  426. X    }
  427. X    return SAVE_DONE;
  428. X}
  429. X
  430. Xint
  431. Xcancel_article()
  432. X{
  433. X    char *artid_buf;
  434. X    char *ngs_buf;
  435. X    char *from_buf;
  436. X    char *reply_buf;
  437. X    int myuid = getuid();
  438. X    int r = -1;
  439. X
  440. X    if (artopen(art) == Nullfp) {
  441. X#ifdef VERBOSE
  442. X    IF(verbose)
  443. X        fputs("\n\
  444. XCancelling null articles is your idea of fun?  :-)\n\
  445. X",stdout) FLUSH;
  446. X    ELSE
  447. X#endif
  448. X#ifdef TERSE
  449. X        fputs(nullart,stdout) FLUSH;
  450. X#endif
  451. X    return r;
  452. X    }
  453. X    reply_buf = fetchlines(art,REPLY_LINE);
  454. X    from_buf = fetchlines(art,FROM_LINE);
  455. X    artid_buf = fetchlines(art,ARTID_LINE);
  456. X    ngs_buf = fetchlines(art,NGS_LINE);
  457. X    if (!instr(from_buf,sitename) ||
  458. X    (!instr(from_buf,logname) &&
  459. X     !instr(reply_buf,logname) &&
  460. X#ifdef NEWSADMIN
  461. X     myuid != newsuid &&
  462. X#endif
  463. X     myuid != ROOTID ) )
  464. X#ifdef VERBOSE
  465. X        IF(verbose)
  466. X        fputs("\nYou can't cancel someone else's article\n",stdout)
  467. X          FLUSH;
  468. X        ELSE
  469. X#endif
  470. X#ifdef TERSE
  471. X        fputs("\nNot your article\n",stdout) FLUSH;
  472. X#endif
  473. X    else {
  474. X    tmpfp = fopen(headname,"w");    /* open header file */
  475. X    if (tmpfp == Nullfp) {
  476. X        printf(cantcreate,headname) FLUSH;
  477. X        goto no_cancel;
  478. X    }
  479. X    interp(buf, (sizeof buf), getval("CANCELHEADER",CANCELHEADER));
  480. X    fputs(buf,tmpfp);
  481. X    fclose(tmpfp);
  482. X    fputs("\nCanceling...\n",stdout) FLUSH;
  483. X    r = doshell(sh,filexp(getval("CANCEL",CANCEL)));
  484. X    }
  485. Xno_cancel:
  486. X    free(artid_buf);
  487. X    free(ngs_buf);
  488. X    free(from_buf);
  489. X    free(reply_buf);
  490. X    return r;
  491. X}
  492. X
  493. Xvoid
  494. Xreply()
  495. X{
  496. X    bool incl_body = (*buf == 'R');
  497. X    char *maildoer = savestr(filexp(getval("MAILPOSTER",MAILPOSTER)));
  498. X
  499. X    artopen(art);
  500. X    tmpfp = fopen(headname,"w");    /* open header file */
  501. X    if (tmpfp == Nullfp) {
  502. X    printf(cantcreate,headname) FLUSH;
  503. X    goto no_reply;
  504. X    }
  505. X    interp(buf, (sizeof buf), getval("MAILHEADER",MAILHEADER));
  506. X    fputs(buf,tmpfp);
  507. X    if (!instr(maildoer,"%h"))
  508. X#ifdef VERBOSE
  509. X    IF(verbose)
  510. X        printf("\n%s\n(Above lines saved in file %s)\n",buf,headname)
  511. X          FLUSH;
  512. X    ELSE
  513. X#endif
  514. X#ifdef TERSE
  515. X        printf("\n%s\n(Header in %s)\n",buf,headname) FLUSH;
  516. X#endif
  517. X    if (incl_body && artfp != Nullfp) {
  518. X    interp(buf, (sizeof buf), getval("YOUSAID",YOUSAID));
  519. X    fprintf(tmpfp,"%s\n",buf);
  520. X#ifdef ASYNC_PARSE
  521. X    parse_maybe(art);
  522. X#endif
  523. X    fseek(artfp,(long)htype[PAST_HEADER].ht_minpos,0);
  524. X    while (fgets(buf,LBUFLEN,artfp) != Nullch) {
  525. X        fprintf(tmpfp,"%s%s",indstr,buf);
  526. X    }
  527. X    fprintf(tmpfp,"\n");
  528. X    }
  529. X    fclose(tmpfp);
  530. X    interp(cmd_buf, (sizeof cmd_buf), maildoer);
  531. X    invoke(cmd_buf,origdir);
  532. X    UNLINK(headname);        /* kill the header file */
  533. Xno_reply:
  534. X    free(maildoer);
  535. X}
  536. X
  537. Xvoid
  538. Xfollowup()
  539. X{
  540. X    bool incl_body = (*buf == 'F');
  541. X    char hbuf[4*LBUFLEN];    /* four times the old size */
  542. X
  543. X    artopen(art);
  544. X    tmpfp = fopen(headname,"w");
  545. X    if (tmpfp == Nullfp) {
  546. X    printf(cantcreate,headname) FLUSH;
  547. X    return;
  548. X    }
  549. X    interp(hbuf, (sizeof hbuf), getval("NEWSHEADER",NEWSHEADER));
  550. X    fprintf(tmpfp,"%s",hbuf);
  551. X    if (incl_body && artfp != Nullfp) {
  552. X#ifdef VERBOSE
  553. X    if (verbose)
  554. X        fputs("\n\
  555. X(Be sure to double-check the attribution against the signature, and\n\
  556. Xtrim the quoted article down as much as possible.)\n\
  557. X",stdout) FLUSH;
  558. X#endif
  559. X    interp(buf, (sizeof buf), getval("ATTRIBUTION",ATTRIBUTION));
  560. X    fprintf(tmpfp,"%s\n",buf);
  561. X#ifdef ASYNC_PARSE
  562. X    parse_maybe(art);
  563. X#endif
  564. X    fseek(artfp,(long)htype[PAST_HEADER].ht_minpos,0);
  565. X    while (fgets(buf,LBUFLEN,artfp) != Nullch) {
  566. X        fprintf(tmpfp,"%s%s",indstr,buf);
  567. X    }
  568. X    fprintf(tmpfp,"\n");
  569. X    }
  570. X    fclose(tmpfp);
  571. X    safecpy(cmd_buf,filexp(getval("NEWSPOSTER",NEWSPOSTER)),sizeof cmd_buf);
  572. X    invoke(cmd_buf,origdir);
  573. X    UNLINK(headname);
  574. X}
  575. X
  576. Xvoid
  577. Xinvoke(cmd,dir)
  578. Xchar *cmd,*dir;
  579. X{
  580. X    if (chdir(dir)) {
  581. X    printf(nocd,dir) FLUSH;
  582. X    return;
  583. X    }
  584. X#ifdef VERBOSE
  585. X    IF(verbose)
  586. X    printf("\n(leaving cbreak mode; cwd=%s)\nInvoking command: %s\n\n",
  587. X        dir,cmd) FLUSH;
  588. X    ELSE
  589. X#endif
  590. X#ifdef TERSE
  591. X    printf("\n(-cbreak; cwd=%s)\nInvoking: %s\n\n",dir,cmd) FLUSH;
  592. X#endif
  593. X    resetty();            /* make terminal well-behaved */
  594. X    doshell(sh,cmd);        /* do the command */
  595. X    noecho();            /* set no echo */
  596. X    crmode();            /* and cbreak mode */
  597. X#ifdef VERBOSE
  598. X    IF(verbose)
  599. X    fputs("\n(re-entering cbreak mode)\n",stdout) FLUSH;
  600. X    ELSE
  601. X#endif
  602. X#ifdef TERSE
  603. X    fputs("\n(+cbreak)\n",stdout) FLUSH;
  604. X#endif
  605. X#ifdef SERVER
  606. X    if (chdir(spool)) {
  607. X#else /* not SERVER */
  608. X    if (chdir(spool) || chdir(ngdir)) {
  609. X#endif /* SERVER */
  610. X    printf(nocd,ngdir) FLUSH;
  611. X    sig_catcher(0);
  612. X    }
  613. X}
  614. X
  615. X/*
  616. X** cut_line() determines if a line is meant as a "cut here" marker.
  617. X** Some examples that we understand:
  618. X**
  619. X**  BEGIN--cut here--cut here
  620. X**
  621. X**  ------------------ tear at this line ------------------
  622. X**
  623. X**  #----cut here-----cut here-----cut here-----cut here----#
  624. X*/
  625. Xbool
  626. Xcut_line(str)
  627. Xchar *str;
  628. X{
  629. X    char *cp, got_flag;
  630. X    char word[80];
  631. X    int  dash_cnt, equal_cnt;
  632. X
  633. X    /* Disallow any single-/double-quoted, parenthetical or c-commented
  634. X    ** string lines.  Make sure it has the cut-phrase and at least 20
  635. X    ** '-'s or '='s.  If only four '-'s are present, check for a duplicate
  636. X    ** of the cut phrase.  If we succeed, return TRUE.
  637. X    */
  638. X    for (cp = str, dash_cnt = equal_cnt = 0; *cp; cp++) {
  639. X    switch (*cp) {
  640. X    case '-':
  641. X        dash_cnt++;
  642. X        break;
  643. X    case '=':
  644. X        equal_cnt++;
  645. X        break;
  646. X    case '/':
  647. X        if( *(cp+1) != '*' ) {
  648. X        break;
  649. X        }
  650. X    case '"':
  651. X    case '\'':
  652. X    case '(':
  653. X    case ')':
  654. X    case '[':
  655. X    case ']':
  656. X    case '{':
  657. X    case '}':
  658. X        return FALSE;
  659. X    }
  660. X    }
  661. X    if (dash_cnt < 4 && equal_cnt < 20)
  662. X    return FALSE;
  663. X
  664. X    got_flag = 0;
  665. X
  666. X    for (*(cp = word) = '\0'; *str; str++) {
  667. X    if (islower(*str))
  668. X        *cp++ = *str;
  669. X    else if (isupper(*str))
  670. X        *cp++ = tolower(*str);
  671. X    else {
  672. X        if (*word) {
  673. X        *cp = '\0';
  674. X        switch (got_flag) {
  675. X        case 2:
  676. X            if (!strcmp(word, "line")
  677. X             || !strcmp(word, "here"))
  678. X            return TRUE;
  679. X            break;
  680. X        case 1:
  681. X            if (!strcmp(word, "this"))
  682. X            got_flag = 2;
  683. X            if (!strcmp(word, "here")) {
  684. X            if (dash_cnt >= 20 || equal_cnt >= 20)
  685. X                return TRUE;
  686. X            dash_cnt = 20;
  687. X            got_flag = 0;
  688. X            }
  689. X            break;
  690. X        case 0:
  691. X            if (!strcmp( word, "cut")
  692. X             || !strcmp( word, "snip")
  693. X             || !strcmp( word, "tear"))
  694. X            got_flag = 1;
  695. X            break;
  696. X        }
  697. X        *(cp = word) = '\0';
  698. X        }
  699. X    }
  700. X    } /* for *str */
  701. X
  702. X    return FALSE;
  703. X}
  704. END_OF_FILE
  705.   if test 16294 -ne `wc -c <'respond.c'`; then
  706.     echo shar: \"'respond.c'\" unpacked with wrong size!
  707.   fi
  708.   # end of 'respond.c'
  709. fi
  710. if test -f 'rt-rn.c' -a "${1}" != "-c" ; then 
  711.   echo shar: Will not clobber existing file \"'rt-rn.c'\"
  712. else
  713.   echo shar: Extracting \"'rt-rn.c'\" \(21485 characters\)
  714.   sed "s/^X//" >'rt-rn.c' <<'END_OF_FILE'
  715. X/* $Header: rt-rn.c,v 4.3.3.1 90/07/28 18:07:55 davison Trn $
  716. X**
  717. X** $Log:    rt-rn.c,v $
  718. X** Revision 4.3.3.1  90/07/28  18:07:55  davison
  719. X** Initial Trn Release
  720. X** 
  721. X*/
  722. X
  723. X#include "EXTERN.h"
  724. X#include "common.h"
  725. X#include "term.h"
  726. X#include "final.h"
  727. X#include "util.h"
  728. X#include "bits.h"
  729. X#include "artio.h"
  730. X#include "ng.h"
  731. X#include "ngdata.h"
  732. X#include "search.h"
  733. X#include "artstate.h"
  734. X#include "backpage.h"
  735. X#include "rthreads.h"
  736. X
  737. X#ifdef USETHREADS
  738. X
  739. Xstatic void find_depth(), cache_tree(), display_tree();
  740. Xstatic char letter();
  741. X
  742. X/* Find the article structure information based on article number.
  743. X*/
  744. Xvoid
  745. Xfind_article( artnum )
  746. XART_NUM artnum;
  747. X{
  748. X    register PACKED_ARTICLE *article;
  749. X    register int i;
  750. X
  751. X    if( !p_articles ) {
  752. X    p_art = Nullart;
  753. X    return;
  754. X    }
  755. X
  756. X    if( !p_art ) {
  757. X    p_art = p_articles;
  758. X    }
  759. X    /* Start looking for the article num from our last known spot in the array.
  760. X    ** That way, if we already know where we are, we run into ourselves right
  761. X    ** away.
  762. X    */
  763. X    for( article=p_art, i=p_art-p_articles; i < total.article; article++,i++ ) {
  764. X    if( article->num == artnum ) {
  765. X        p_art = article;
  766. X        return;
  767. X    }
  768. X    }
  769. X    /* Didn't find it, so search the ones before our current position.
  770. X    */
  771. X    for( article = p_articles; article != p_art; article++ ) {
  772. X    if( article->num == artnum ) {
  773. X        p_art = article;
  774. X        return;
  775. X    }
  776. X    }
  777. X    p_art = Nullart;
  778. X}
  779. X
  780. Xstatic char tree_indent[] = {
  781. X    ' ', 0,
  782. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  783. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  784. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  785. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  786. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  787. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  788. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  789. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  790. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  791. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  792. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  793. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  794. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  795. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0
  796. X};
  797. X
  798. Xchar letters[] = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz?";
  799. X
  800. Xstatic PACKED_ARTICLE *tree_article;
  801. X
  802. Xstatic int max_depth, max_line = -1;
  803. Xstatic int first_depth, first_line;
  804. Xstatic int my_depth, my_line;
  805. Xstatic bool node_on_line;
  806. Xstatic int node_line_cnt;
  807. X
  808. Xstatic int line_num;
  809. Xstatic int header_indent;
  810. X
  811. Xstatic char *tree_lines[11];
  812. Xstatic char tree_buff[128], *str;
  813. X
  814. X/* Prepare tree display for inclusion in the article header.
  815. X*/
  816. Xvoid
  817. Xinit_tree()
  818. X{
  819. X    register PACKED_ARTICLE *article;
  820. X
  821. X#if 000            /* don't do this, since read-status may change */
  822. X    if( curr_p_art == tree_article ) {
  823. X    return;
  824. X    }
  825. X#endif
  826. X    while( max_line >= 0 ) {        /* free any previous tree data */
  827. X    free( tree_lines[max_line--] );
  828. X    }
  829. X    tree_article = curr_p_art;
  830. X
  831. X    if( !curr_p_art ) {
  832. X    return;
  833. X    }
  834. X    article = p_articles + p_roots[curr_p_art->root].articles;
  835. X
  836. X    max_depth = max_line = my_depth = my_line = node_line_cnt = 0;
  837. X    find_depth( article, 0 );
  838. X
  839. X    if( max_depth <= 5 ) {
  840. X    first_depth = 0;
  841. X    } else {
  842. X    if( my_depth+2 > max_depth ) {
  843. X        first_depth = max_depth - 5;
  844. X    } else if( (first_depth = my_depth - 3) < 0 ) {
  845. X        first_depth = 0;
  846. X    }
  847. X    max_depth = first_depth + 5;
  848. X    }
  849. X    if( --max_line < max_tree_lines ) {
  850. X    first_line = 0;
  851. X    } else {
  852. X    if( my_line + max_tree_lines/2 > max_line ) {
  853. X        first_line = max_line - (max_tree_lines-1);
  854. X    } else if( (first_line = my_line - (max_tree_lines-1)/2) < 0 ) {
  855. X        first_line = 0;
  856. X    }
  857. X    max_line = first_line + max_tree_lines-1;
  858. X    }
  859. X
  860. X    str = tree_buff;        /* initialize first line's data */
  861. X    *str++ = ' ';
  862. X    node_on_line = FALSE;
  863. X    line_num = 0;
  864. X    /* cache our portion of the tree */
  865. X    cache_tree( article, 0, tree_indent );
  866. X
  867. X    max_depth = (max_depth-first_depth) * 5;    /* turn depth into char width */
  868. X    max_line -= first_line;            /* turn max_line into count */
  869. X    /* shorten tree if lower lines aren't visible */
  870. X    if( node_line_cnt < max_line ) {
  871. X    max_line = node_line_cnt + 1;
  872. X    }
  873. X}
  874. X
  875. X/* A recursive routine to find the maximum tree extents and where we are.
  876. X*/
  877. Xstatic void
  878. Xfind_depth( article, depth )
  879. XPACKED_ARTICLE *article;
  880. X{
  881. X    if( depth > max_depth ) {
  882. X    max_depth = depth;
  883. X    }
  884. X    for( ;; ) {
  885. X    if( article == tree_article ) {
  886. X        my_depth = depth;
  887. X        my_line = max_line;
  888. X    }
  889. X    if( article->child_cnt ) {
  890. X        find_depth( article+1, depth+1 );
  891. X    } else {
  892. X        max_line++;
  893. X    }
  894. X    if( !article->siblings ) {
  895. X        break;
  896. X    }
  897. X    article += article->siblings;
  898. X    }
  899. X}
  900. X
  901. X/* Place the tree display in a maximum of 11 lines x 6 nodes.
  902. X*/
  903. Xstatic void
  904. Xcache_tree( article, depth, cp )
  905. XPACKED_ARTICLE *article;
  906. Xint depth;
  907. Xchar *cp;
  908. X{
  909. X    int depth_mode;
  910. X
  911. X    cp[1] = ' ';
  912. X    if( depth >= first_depth && depth <= max_depth ) {
  913. X    cp += 5;
  914. X    depth_mode = 1;
  915. X    } else if( depth+1 == first_depth ) {
  916. X    depth_mode = 2;
  917. X    } else {
  918. X    cp = tree_indent;
  919. X    depth_mode = 0;
  920. X    }
  921. X    for( ;; ) {
  922. X    switch( depth_mode ) {
  923. X    case 1: {
  924. X        char ch;
  925. X
  926. X        *str++ = (article->flags & ROOT_ARTICLE)? ' ' : '-';
  927. X        if( article == tree_article ) {
  928. X        *str++ = '*';
  929. X        }
  930. X        if( was_read( article->num ) ) {
  931. X        *str++ = '(';
  932. X        ch = ')';
  933. X        } else {
  934. X        *str++ = '[';
  935. X        ch = ']';
  936. X        }
  937. X        if( article == recent_p_art && article != tree_article ) {
  938. X        *str++ = '@';
  939. X        }
  940. X        *str++ = letter( article );
  941. X        *str++ = ch;
  942. X        if( article->child_cnt ) {
  943. X        *str++ = (article->child_cnt == 1)? '-' : '+';
  944. X        }
  945. X        if( article->siblings ) {
  946. X        *cp = '|';
  947. X        } else {
  948. X        *cp = ' ';
  949. X        }
  950. X        node_on_line = TRUE;
  951. X        break;
  952. X    }
  953. X    case 2:
  954. X        *tree_buff = (!article->child_cnt)? ' ' :
  955. X        (article->child_cnt == 1)? '-' : '+';
  956. X        break;
  957. X    default:
  958. X        break;
  959. X    }
  960. X    if( article->child_cnt ) {
  961. X        cache_tree( article+1, depth+1, cp );
  962. X        cp[1] = '\0';
  963. X    } else {
  964. X        if( !node_on_line && first_line == line_num ) {
  965. X        first_line++;
  966. X        }
  967. X        if( line_num >= first_line ) {
  968. X        if( str[-1] == ' ' ) {
  969. X            str--;
  970. X        }
  971. X        *str = '\0';
  972. X        tree_lines[line_num-first_line]
  973. X            = safemalloc( str-tree_buff + 1 );
  974. X        strcpy( tree_lines[line_num - first_line], tree_buff );
  975. X        if( node_on_line ) {
  976. X            node_line_cnt = line_num - first_line;
  977. X        }
  978. X        }
  979. X        line_num++;
  980. X        node_on_line = FALSE;
  981. X    }
  982. X    if( !article->siblings || line_num > max_line ) {
  983. X        break;
  984. X    }
  985. X    article += article->siblings;
  986. X    if( !article->siblings ) {
  987. X        *cp = '\\';
  988. X    }
  989. X    if( !first_depth ) {
  990. X        tree_indent[5] = ' ';
  991. X    }
  992. X    strcpy( tree_buff, tree_indent+5 );
  993. X    str = tree_buff + strlen( tree_buff );
  994. X    }
  995. X}
  996. X
  997. X/* Output a header line with possible tree display on the right hand side.
  998. X** Does automatic wrapping of lines that are too long.
  999. X*/
  1000. Xint
  1001. Xtree_puts( orig_line, header_line, use_underline )
  1002. Xchar *orig_line;
  1003. XART_LINE header_line;
  1004. Xint use_underline;
  1005. X{
  1006. X    char *buf;
  1007. X    register char *line, *cp, *end;
  1008. X    int pad_cnt, wrap_at;
  1009. X    ART_LINE start_line = header_line;
  1010. X    int i;
  1011. X    char ch;
  1012. X
  1013. X    /* Make a modifiable copy of the line */
  1014. X    buf = safemalloc( strlen( orig_line ) + 2 );  /* yes, I mean "2" */
  1015. X    strcpy( buf, orig_line );
  1016. X    line = buf;
  1017. X
  1018. X    /* Change any embedded control characters to spaces */
  1019. X    for( end = line; *end && *end != '\n'; end++ ) {
  1020. X    if( (unsigned char)*end < ' ' ) {
  1021. X        *end = ' ';
  1022. X    }
  1023. X    }
  1024. X    *end = '\0';
  1025. X
  1026. X    if( !*line ) {
  1027. X    strcpy( line, " " );
  1028. X    end = line+1;
  1029. X    }
  1030. X
  1031. X    /* If this is the first subject line, output it with a preceeding [1] */
  1032. X    if( use_underline && curr_p_art && (unsigned char)*line > ' ' ) {
  1033. X#ifdef NOFIREWORKS
  1034. X    no_sofire();
  1035. X#endif
  1036. X    standout();
  1037. X    putchar( '[' );
  1038. X    putchar( letter( curr_p_art ) );
  1039. X    putchar( ']' );
  1040. X    un_standout();
  1041. X    putchar( ' ' );
  1042. X    header_indent = 4;
  1043. X    line += 9;
  1044. X    i = 0;
  1045. X    } else {
  1046. X    if( *line != ' ' ) {
  1047. X        /* A "normal" header line -- output keyword and set header_indent
  1048. X        ** _except_ for the first line, which is a non-standard header.
  1049. X        */
  1050. X        if( !header_line || !(cp = index( line, ':' )) || *++cp != ' ' ) {
  1051. X        header_indent = 0;
  1052. X        } else {
  1053. X        *cp = '\0';
  1054. X        fputs( line, stdout );
  1055. X        putchar( ' ' );
  1056. X        header_indent = ++cp - line;
  1057. X        line = cp;
  1058. X        }
  1059. X        i = 0;
  1060. X    } else {
  1061. X        /* Skip whitespace of continuation lines and prepare to indent */
  1062. X        while( *++line == ' ' ) {
  1063. X        ;
  1064. X        }
  1065. X        i = header_indent;
  1066. X    }
  1067. X    }
  1068. X    for( ; *line; i = header_indent ) {
  1069. X#ifdef CLEAREOL
  1070. X    maybe_eol();
  1071. X#endif
  1072. X    if( i ) {
  1073. X        putchar( '+' );
  1074. X        while( --i ) {
  1075. X        putchar( ' ' );
  1076. X        }
  1077. X    }
  1078. X    /* If no (more) tree lines, wrap at COLS-1 */
  1079. X    if( max_line < 0 || header_line > max_line+1 ) {
  1080. X        wrap_at = COLS-1;
  1081. X    } else {
  1082. X        wrap_at = COLS - max_depth - 5 - 3;
  1083. X    }
  1084. X    /* Figure padding between header and tree output, wrapping long lines */
  1085. X    pad_cnt = wrap_at - (end - line + header_indent);
  1086. X    if( pad_cnt <= 0 ) {
  1087. X        cp = line + wrap_at - header_indent - 1;
  1088. X        pad_cnt = 1;
  1089. X        while( cp > line && *cp != ' ' ) {
  1090. X        if( *--cp == ',' || *cp == '.' || *cp == '-' || *cp == '!' ) {
  1091. X            cp++;
  1092. X            break;
  1093. X        }
  1094. X        pad_cnt++;
  1095. X        }
  1096. X        if( cp == line ) {
  1097. X        cp += wrap_at - header_indent;
  1098. X        pad_cnt = 0;
  1099. X        }
  1100. X        ch = *cp;
  1101. X        *cp = '\0';
  1102. X        /* keep rn's backpager happy */
  1103. X        vwtary( artline, vrdary( artline - 1 ) );
  1104. X        artline++;
  1105. X    } else {
  1106. X        cp = end;
  1107. X        ch = '\0';
  1108. X    }
  1109. X    if( use_underline ) {
  1110. X        underprint( line );
  1111. X    } else {
  1112. X        fputs( line, stdout );
  1113. X    }
  1114. X    *cp = ch;
  1115. X    /* Skip whitespace in wrapped line */
  1116. X    while( *cp == ' ' ) {
  1117. X        cp++;
  1118. X    }
  1119. X    line = cp;
  1120. X    /* Check if we've got any tree lines to output */
  1121. X    if( wrap_at != COLS-1 && header_line <= max_line ) {
  1122. X        char *cp1, *cp2;
  1123. X
  1124. X        do {
  1125. X        putchar( ' ' );
  1126. X        } while( pad_cnt-- );
  1127. X        /* Check string for the '*' flagging our current node
  1128. X        ** and the '@' flagging our prior node.
  1129. X        */
  1130. X        cp = tree_lines[header_line];
  1131. X        cp1 = index( cp, '*' );
  1132. X        cp2 = index( cp, '@' );
  1133. X        if( cp1 != Nullch ) {
  1134. X        *cp1 = '\0';
  1135. X        }
  1136. X        if( cp2 != Nullch ) {
  1137. X        *cp2 = '\0';
  1138. X        }
  1139. X        fputs( cp, stdout );
  1140. X        /* Handle standout output for '*' and '@' marked nodes, then
  1141. X        ** continue with the rest of the line.
  1142. X        */
  1143. X        while( cp1 || cp2 ) {
  1144. X        standout();
  1145. X        if( cp1 && (!cp2 || cp1 < cp2) ) {
  1146. X            cp = cp1;
  1147. X            cp1 = Nullch;
  1148. X            *cp++ = '*';
  1149. X            putchar( *cp++ );
  1150. X            putchar( *cp++ );
  1151. X        } else {
  1152. X            cp = cp2;
  1153. X            cp2 = Nullch;
  1154. X            *cp++ = '@';
  1155. X        }
  1156. X        putchar( *cp++ );
  1157. X        un_standout();
  1158. X        if( *cp ) {
  1159. X            fputs( cp, stdout );
  1160. X        }
  1161. X        }/* while */
  1162. X    }/* if */
  1163. X    putchar( '\n' ) FLUSH;
  1164. X    header_line++;
  1165. X    }/* for remainder of line */
  1166. X
  1167. X    /* free allocated copy of line */
  1168. X    free( buf );
  1169. X
  1170. X    /* return number of lines displayed */
  1171. X    return header_line - start_line;
  1172. X}
  1173. X
  1174. X/* Output any parts of the tree that are left to display.  Called at the
  1175. X** end of each header.
  1176. X*/
  1177. Xint
  1178. Xfinish_tree( last_line )
  1179. XART_LINE last_line;
  1180. X{
  1181. X    ART_LINE start_line = last_line;
  1182. X
  1183. X    while( last_line <= max_line ) {
  1184. X    artline++;
  1185. X    last_line += tree_puts( "+", last_line, 0 );
  1186. X    vwtary( artline, artpos );    /* keep rn's backpager happy */
  1187. X    }
  1188. X    return last_line - start_line;
  1189. X}
  1190. X
  1191. X/* Output the entire article tree for the user.
  1192. X*/
  1193. Xvoid
  1194. Xentire_tree()
  1195. X{
  1196. X    int j, root;
  1197. X
  1198. X    if( check_page_line() ) {
  1199. X    return;
  1200. X    }
  1201. X    if( !p_art ) {
  1202. X#ifdef VERBOSE
  1203. X    IF( verbose )
  1204. X        fputs( "\nNo article tree to display.\n", stdout );
  1205. X    ELSE
  1206. X#endif
  1207. X#ifdef TERSE
  1208. X        fputs( "\nNo tree.\n", stdout );
  1209. X#endif
  1210. X    } else {
  1211. X    root = p_art->root;
  1212. X#ifdef NOFIREWORKS
  1213. X    no_sofire();
  1214. X#endif
  1215. X    standout();
  1216. X    printf( "T%ld:\n", (long)p_roots[root].root_num );
  1217. X    un_standout();
  1218. X    if( check_page_line() ) {
  1219. X        return;
  1220. X    }
  1221. X    putchar( '\n' );
  1222. X    for( j = 0; j < p_roots[root].subject_cnt; j++ ) {
  1223. X        sprintf( buf, "[%c] %s\n", letters[j > 9+26+26 ? 9+26+26 : j],
  1224. X        subject_ptrs[root_subjects[root]+j] );
  1225. X        if( check_page_line() ) {
  1226. X        return;
  1227. X        }
  1228. X        fputs( buf, stdout );
  1229. X    }
  1230. X    if( check_page_line() ) {
  1231. X        return;
  1232. X    }
  1233. X    putchar( '\n' );
  1234. X    if( check_page_line() ) {
  1235. X        return;
  1236. X    }
  1237. X    putchar( ' ' );
  1238. X    buf[3] = '\0';
  1239. X    display_tree( p_articles+p_roots[p_art->root].articles, tree_indent );
  1240. X
  1241. X    if( check_page_line() ) {
  1242. X        return;
  1243. X    }
  1244. X    putchar( '\n' );
  1245. X    }
  1246. X}
  1247. X
  1248. X/* A recursive routine to output the entire article tree.
  1249. X*/
  1250. Xstatic void
  1251. Xdisplay_tree( article, cp )
  1252. XPACKED_ARTICLE *article;
  1253. Xchar *cp;
  1254. X{
  1255. X    if( cp - tree_indent > COLS || page_line < 0 ) {
  1256. X    return;
  1257. X    }
  1258. X    cp[1] = ' ';
  1259. X    cp += 5;
  1260. X    for( ;; ) {
  1261. X    putchar( (article->flags & ROOT_ARTICLE)? ' ' : '-' );
  1262. X    if( was_read( article->num ) ) {
  1263. X        buf[0] = '(';
  1264. X        buf[2] = ')';
  1265. X    } else {
  1266. X        buf[0] = '[';
  1267. X        buf[2] = ']';
  1268. X    }
  1269. X    buf[1] = letter( article );
  1270. X    if( article == curr_p_art ) {
  1271. X        standout();
  1272. X        fputs( buf, stdout );
  1273. X        un_standout();
  1274. X    } else if( article == recent_p_art ) {
  1275. X        putchar( buf[0] );
  1276. X        standout();
  1277. X        putchar( buf[1] );
  1278. X        un_standout();
  1279. X        putchar( buf[2] );
  1280. X    } else {
  1281. X        fputs( buf, stdout );
  1282. X    }
  1283. X
  1284. X    if( article->siblings ) {
  1285. X        *cp = '|';
  1286. X    } else {
  1287. X        *cp = ' ';
  1288. X    }
  1289. X    if( article->child_cnt ) {
  1290. X        putchar( (article->child_cnt == 1)? '-' : '+' );
  1291. X        display_tree( article+1, cp );
  1292. X        cp[1] = '\0';
  1293. X    } else {
  1294. X        putchar( '\n' ) FLUSH;
  1295. X    }
  1296. X    if( !article->siblings ) {
  1297. X        break;
  1298. X    }
  1299. X    article += article->siblings;
  1300. X    if( !article->siblings ) {
  1301. X        *cp = '\\';
  1302. X    }
  1303. X    tree_indent[5] = ' ';
  1304. X    if( check_page_line() ) {
  1305. X        return;
  1306. X    }
  1307. X    fputs( tree_indent+5, stdout );
  1308. X    }
  1309. X}
  1310. X
  1311. Xint
  1312. Xcheck_page_line()
  1313. X{
  1314. X    if( page_line < 0 ) {
  1315. X    return -1;
  1316. X    }
  1317. X    if( page_line >= LINES || int_count ) {
  1318. X      register int cmd = -1;
  1319. X    if( int_count || (cmd = get_anything()) ) {
  1320. X        page_line = -1;        /* disable further printing */
  1321. X        if( cmd > 0 ) {
  1322. X        pushchar( cmd );
  1323. X        }
  1324. X        return cmd;
  1325. X    }
  1326. X    }
  1327. X    page_line++;
  1328. X    return 0;
  1329. X}
  1330. X
  1331. X/* Calculate the subject letter representation.  "Place-holder" nodes
  1332. X** are marked with a ' ', others get a letter in the sequence:
  1333. X**    ' ', '1'-'9', 'A'-'Z', 'a'-'z', '?'
  1334. X*/
  1335. Xstatic char
  1336. Xletter( article )
  1337. XPACKED_ARTICLE *article;
  1338. X{
  1339. X    register int subj = article->subject;
  1340. X
  1341. X    if( subj < 0 ) {
  1342. X    return ' ';
  1343. X    }
  1344. X    subj -= root_subjects[article->root];
  1345. X    if( subj < 9+26+26 ) {
  1346. X    return letters[subj];
  1347. X    }
  1348. X    return '?';
  1349. X}
  1350. X
  1351. X/* Find the first unread article in the (possibly selected) root order.
  1352. X*/
  1353. Xvoid
  1354. Xfirst_art()
  1355. X{
  1356. X    register int r;
  1357. X
  1358. X    if( !ThreadedGroup ) {
  1359. X    art = firstart;
  1360. X    return;
  1361. X    }
  1362. X    p_art = Nullart;
  1363. X    art = lastart+1;
  1364. X    follow_thread( 'n' );
  1365. X}
  1366. X
  1367. X/* Perform a command over all or a section of the article tree.  Most of
  1368. X** the option letters match commands entered from article mode:
  1369. X**   n - find the next unread article after current article.
  1370. X**  ^N - find the next unread article with the same subject.
  1371. X**   N - goto the next article in the thread.
  1372. X**   J - junk the entire thread.
  1373. X**   k - junk all articles with this same subject.
  1374. X**   K - kill all this article's descendants (we know that the caller
  1375. X**     killed the current article on the way here).
  1376. X**   u - mark entire thread as "unread".
  1377. X**   U - mark this article and its descendants as "unread".
  1378. X**   f - follow the thread (like 'n'), but don't attempt to find a new thread
  1379. X**     if we run off the end.
  1380. X*/
  1381. Xvoid
  1382. Xfollow_thread( cmd )
  1383. Xchar cmd;
  1384. X{
  1385. X    int curr_subj = -1, sel;
  1386. X    PACKED_ARTICLE *root_limit, *p_art_old = Nullart;
  1387. X    bool subthread_flag;
  1388. X
  1389. X    reread = FALSE;
  1390. X
  1391. X    if( !p_art ) {
  1392. X    if( ThreadedGroup && art > lastart ) {
  1393. X        p_art = root_limit = p_articles;
  1394. X        goto follow_root;
  1395. X    }
  1396. X    art++;
  1397. X    return;
  1398. X    }
  1399. X    if( cmd == 'k' || cmd == Ctl('n') ) {
  1400. X    if( (curr_subj = p_art->subject) == -1) {
  1401. X        return;
  1402. X    }
  1403. X    p_art_old = p_art;
  1404. X    }
  1405. X    sel = (selected_roots[p_art->root] & 1);
  1406. X    if( cmd == 'U' || cmd == 'K' ) {
  1407. X    subthread_flag = TRUE;
  1408. X    p_art_old = p_art;
  1409. X    } else {
  1410. X    subthread_flag = FALSE;
  1411. X    }
  1412. X    /* The current article is already marked as read for 'K' */
  1413. X    if( cmd == 'k' || cmd == 'J' || cmd == 'u' ) {
  1414. X    p_art = p_articles + p_roots[p_art->root].articles;
  1415. X    art = p_art->num;
  1416. X    if( cmd == 'u' ) {
  1417. X        p_art_old = p_art;
  1418. X        cmd = 'U';
  1419. X    } else {
  1420. X        if( !was_read( art )
  1421. X         && (curr_subj < 0 || curr_subj == p_art->subject) ) {
  1422. X        set_read( art, sel );
  1423. X        }
  1424. X        cmd = 'K';
  1425. X    }
  1426. X    }
  1427. X    if( cmd == 'U' ) {
  1428. X    if( p_art->subject != -1 ) {
  1429. X        set_unread( art, sel );
  1430. X    }
  1431. X    root_article_cnts[p_art->root] = 1;
  1432. X    scan_all_roots = FALSE;
  1433. X    }
  1434. X  follow_again:
  1435. X    sel = (selected_roots[p_art->root] & 1);
  1436. X    root_limit = upper_limit( p_art, subthread_flag );
  1437. X    for( ;; ) {
  1438. X    if( ++p_art == root_limit ) {
  1439. X        break;
  1440. X    }
  1441. X    if( !(art = p_art->num) ) {
  1442. X        continue;
  1443. X    }
  1444. X    if( cmd == 'K' || p_art->subject == -1 ) {
  1445. X        if( !was_read( art )
  1446. X         && (curr_subj < 0 || curr_subj == p_art->subject) ) {
  1447. X        set_read( art, sel );
  1448. X        }
  1449. X    } else if( cmd == 'U' ) {
  1450. X        set_unread( art, sel );
  1451. X    } else if( !was_read( art )
  1452. X        && (curr_subj < 0 || curr_subj == p_art->subject) ) {
  1453. X        return;
  1454. X    } else if( cmd == 'N' ) {
  1455. X        reread = TRUE;
  1456. X        return;
  1457. X    }
  1458. X    }/* for */
  1459. X    if( p_art_old ) {
  1460. X    p_art = p_art_old;
  1461. X    if( cmd == 'U' && p_art->subject != -1 ) {
  1462. X        art = p_art->num;
  1463. X        return;
  1464. X    }
  1465. X    p_art_old = Nullart;
  1466. X    cmd = 'n';
  1467. X    curr_subj = -1;
  1468. X    subthread_flag = FALSE;
  1469. X    goto follow_again;
  1470. X    }
  1471. X    if( cmd == 'f' ) {
  1472. X    p_art = Nullart;
  1473. X    art = lastart+1;
  1474. X    return;
  1475. X    }
  1476. X  follow_root:
  1477. X    if( root_limit != p_articles + total.article ) {
  1478. X    register int r;
  1479. X
  1480. X    for( r = p_art->root; r < total.root; r++ ) {
  1481. X        if( !selected_root_cnt || selected_roots[r] ) {
  1482. X        p_art = p_articles + p_roots[r].articles;
  1483. X        art = p_art->num;
  1484. X        if( p_art->subject == -1 || (cmd != 'N' && was_read( art )) ) {
  1485. X            if( cmd != 'N' ) {
  1486. X            cmd = 'n';
  1487. X            }
  1488. X            curr_subj = -1;
  1489. X            subthread_flag = FALSE;
  1490. X            goto follow_again;
  1491. X        }
  1492. X        return;
  1493. X        }
  1494. X    }
  1495. X    }
  1496. X    if( !count_roots( FALSE ) && unthreaded ) {
  1497. X    /* No threaded articles left -- blow everything else away */
  1498. X    for( art = firstart; art <= lastart; art++ ) {
  1499. X        oneless( art );
  1500. X    }
  1501. X    unthreaded = 0;
  1502. X    }
  1503. X    p_art = Nullart;
  1504. X    art = lastart+1;
  1505. X}
  1506. X
  1507. X/* Go backward in the article tree.  Options match commands in article mode:
  1508. X**    p - previous unread article.
  1509. X**   ^P - previous unread article with same subject.
  1510. X**    P - previous article.
  1511. X*/
  1512. Xvoid
  1513. Xbacktrack_thread( cmd )
  1514. Xchar cmd;
  1515. X{
  1516. X    int curr_subj = -1, sel;
  1517. X    PACKED_ARTICLE *root_limit, *p_art_old = Nullart;
  1518. X
  1519. X    if( art > lastart ) {
  1520. X    p_art = p_articles + total.article - 1;
  1521. X    root_limit = Nullart;
  1522. X    goto backtrack_root;
  1523. X    }
  1524. X    if( !p_art ) {
  1525. X    art--;
  1526. X    return;
  1527. X    }
  1528. X    if( cmd == Ctl('p') ) {
  1529. X    if( (curr_subj = p_art->subject) == -1) {
  1530. X        return;
  1531. X    }
  1532. X    p_art_old = p_art;
  1533. X    }
  1534. X  backtrack_again:
  1535. X    sel = (selected_roots[p_art->root] & 1);
  1536. X    root_limit = p_articles + p_roots[p_art->root].articles;
  1537. X    for( ;; ) {
  1538. X    if( p_art-- == root_limit ) {
  1539. X        break;
  1540. X    }
  1541. X    if( !(art = p_art->num) ) {
  1542. X        continue;
  1543. X    }
  1544. X    if( p_art->subject == -1 ) {
  1545. X        set_read( art, sel );
  1546. X    } else if( !was_read( art )
  1547. X        && (curr_subj < 0 || curr_subj == p_art->subject) ) {
  1548. X        return;
  1549. X    } else if( cmd == 'P' ) {
  1550. X        reread = TRUE;
  1551. X        return;
  1552. X    }
  1553. X    }/* for */
  1554. X    if( p_art_old ) {
  1555. X    p_art = p_art_old;
  1556. X    p_art_old = Nullart;
  1557. X    curr_subj = -1;
  1558. X    goto backtrack_again;
  1559. X    }
  1560. X  backtrack_root:
  1561. X    if( root_limit != p_articles ) {
  1562. X    register int r;
  1563. X
  1564. X    for( r = p_art->root; r >= 0; r-- ) {
  1565. X        if( !selected_root_cnt || selected_roots[r] ) {
  1566. X        art = p_art->num;
  1567. X        if( cmd != 'P' && was_read( art ) ) {
  1568. X            goto backtrack_again;
  1569. X        }
  1570. X        return;
  1571. X        }
  1572. X        p_art = p_articles + p_roots[r].articles - 1;
  1573. X    }
  1574. X    }
  1575. X    p_art = Nullart;
  1576. X    art = absfirst-1;
  1577. X}
  1578. X
  1579. X/* Find the next root (first if p_art == NULL).  If roots are selected,
  1580. X** only choose from selected roots.
  1581. X*/
  1582. Xvoid
  1583. Xnext_root()
  1584. X{
  1585. X    register int r;
  1586. X
  1587. X    reread = FALSE;
  1588. X
  1589. X    if( p_art ) {
  1590. X    r = p_art->root+1;
  1591. X    } else {
  1592. X    r = 0;
  1593. X    }
  1594. X    for( ; r < total.root; r++ ) {
  1595. X    if( !selected_root_cnt || selected_roots[r] ) {
  1596. X      try_again:
  1597. X        p_art = p_articles + p_roots[r].articles;
  1598. X        art = p_art->num;
  1599. X        if( p_art->subject == -1 || (!reread && was_read( art )) ) {
  1600. X        follow_thread( reread ? 'N' : 'f' );
  1601. X        if( art == lastart+1 ) {
  1602. X            if( scan_all_roots || selected_root_cnt
  1603. X             || root_article_cnts[r] ) {
  1604. X            reread = TRUE;
  1605. X            goto try_again;
  1606. X            }
  1607. X            continue;
  1608. X        }
  1609. X        }
  1610. X        return;
  1611. X    }
  1612. X    }
  1613. X    p_art = Nullart;
  1614. X    art = lastart+1;
  1615. X    forcelast = TRUE;
  1616. X}
  1617. X
  1618. X/* Find previous root (or last if p_art == NULL).  If roots are selected,
  1619. X** only choose from selected roots.
  1620. X*/
  1621. Xvoid
  1622. Xprev_root()
  1623. X{
  1624. X    register int r;
  1625. X
  1626. X    reread = FALSE;
  1627. X
  1628. X    if( p_art ) {
  1629. X    r = p_art->root - 1;
  1630. X    } else {
  1631. X    r = total.root - 1;
  1632. X    }
  1633. X    for( ; r >= 0; r-- ) {
  1634. X    if( !selected_root_cnt || selected_roots[r] ) {
  1635. X      try_again:
  1636. X        p_art = p_articles + p_roots[r].articles;
  1637. X        art = p_art->num;
  1638. X        if( p_art->subject == -1 || (!reread && was_read( art )) ) {
  1639. X        follow_thread( reread ? 'N' : 'f' );
  1640. X        if( art == lastart+1 ) {
  1641. X            if( scan_all_roots || selected_root_cnt
  1642. X             || root_article_cnts[r] ) {
  1643. X            reread = TRUE;
  1644. X            goto try_again;
  1645. X            }
  1646. X            continue;
  1647. X        }
  1648. X        }
  1649. X        return;
  1650. X    }
  1651. X    }
  1652. X    p_art = Nullart;
  1653. X    art = lastart+1;
  1654. X    forcelast = TRUE;
  1655. X}
  1656. X
  1657. X/* Return a pointer value that we will equal when we've reached the end of
  1658. X** the current (sub-)thread.
  1659. X*/
  1660. XPACKED_ARTICLE *
  1661. Xupper_limit( artp, subthread_flag )
  1662. XPACKED_ARTICLE *artp;
  1663. Xbool subthread_flag;
  1664. X{
  1665. X    if( subthread_flag ) {
  1666. X    for( ;; ) {
  1667. X        if( artp->siblings ) {
  1668. X        return artp + artp->siblings;
  1669. X        }
  1670. X        if( !artp->parent ) {
  1671. X        break;
  1672. X        }
  1673. X        artp += artp->parent;
  1674. X    }
  1675. X    }
  1676. X    return p_articles + (artp->root == total.root-1 ?
  1677. X    total.article : p_roots[artp->root+1].articles);
  1678. X}
  1679. X
  1680. X#endif /* USETHREADS */
  1681. END_OF_FILE
  1682.   if test 21485 -ne `wc -c <'rt-rn.c'`; then
  1683.     echo shar: \"'rt-rn.c'\" unpacked with wrong size!
  1684.   fi
  1685.   # end of 'rt-rn.c'
  1686. fi
  1687. if test -f 'rt-select.c' -a "${1}" != "-c" ; then 
  1688.   echo shar: Will not clobber existing file \"'rt-select.c'\"
  1689. else
  1690.   echo shar: Extracting \"'rt-select.c'\" \(22344 characters\)
  1691.   sed "s/^X//" >'rt-select.c' <<'END_OF_FILE'
  1692. X/* $Header: rt-select.c,v 4.3.3.3 91/01/16 03:28:50 davison Trn $
  1693. X**
  1694. X** $Log:    rt-select.c,v $
  1695. X** Revision 4.3.3.3  91/01/16  03:28:50  davison
  1696. X** Changed %d --> %ld.
  1697. X** Changed some expressions to registers to bypass a compiler problem.
  1698. X** 
  1699. X** Revision 4.3.3.2  90/08/20  18:32:33  davison
  1700. X** Reset scan_all_roots while selecting.
  1701. X** 
  1702. X** Revision 4.3.3.1  90/07/24  22:04:07  davison
  1703. X** Initial Trn Release
  1704. X** 
  1705. X*/
  1706. X
  1707. X#include "EXTERN.h"
  1708. X#include "common.h"
  1709. X#include "rn.h"
  1710. X#include "rcstuff.h"
  1711. X#include "term.h"
  1712. X#include "final.h"
  1713. X#include "util.h"
  1714. X#include "help.h"
  1715. X#include "bits.h"
  1716. X#include "artsrch.h"
  1717. X#include "ng.h"
  1718. X#include "ngstuff.h"
  1719. X#include "rthreads.h"
  1720. X
  1721. X#ifdef USETHREADS
  1722. X
  1723. Xstatic int count_subj_lines();
  1724. Xstatic void display_subj();
  1725. X
  1726. X/* When display mode is 'l', each author gets a separate line; when 'm', up to
  1727. X** three authors share a line; when 's', no authors are displayed.
  1728. X*/
  1729. Xstatic char *display_mode = select_order;
  1730. Xstatic ART_NUM article_count;
  1731. Xstatic int author_line;
  1732. Xstatic char first_two_chars[3] = { ' ', ' ', '\0' }, mask = 1;
  1733. X
  1734. X#define MAX_SEL 64
  1735. X
  1736. X/* Display a menu of roots for the user to choose from.  If cmd is '+'
  1737. X** we display all the unread roots and allow the user to mark roots as
  1738. X** selected and perform various commands upon the articles.  If cmd is
  1739. X** 'U' we display all the previously read roots and allow the user to
  1740. X** select which ones should be marked as unread.
  1741. X*/
  1742. Xchar
  1743. Xselect_thread( cmd )
  1744. Xchar cmd;
  1745. X{
  1746. X    register int i, j, cnt;
  1747. X    ART_NUM art_hold = art;
  1748. X    int line_cnt, screen_line, subj_line_cnt;
  1749. X    int cur_root, page_root, last_root = -1;
  1750. X    ART_LINE running_total, last_running;
  1751. X    int last_line, got_dash;
  1752. X    int max_root;
  1753. X    int first, last;
  1754. X    int root_line[MAX_SEL], root_hold[MAX_SEL];
  1755. X    int ch, action;
  1756. X    char page_char, end_char;
  1757. X    char promptbuf[80];
  1758. X    bool etc, clean_screen, empty_ok, displayed_status;
  1759. X    char oldmode = mode;
  1760. X#ifndef CONDSUB
  1761. X    char tmpbuf[2];
  1762. X#endif
  1763. X    char *select_chars, *in_select;
  1764. X    int max_cnt;
  1765. X
  1766. X    mode = 't';
  1767. X    unread_selector = (cmd == 'U');
  1768. X    clear_on_stop = TRUE;
  1769. X    empty_ok = FALSE;
  1770. X
  1771. X  select_threads:
  1772. X    /* Setup for selecting articles to read or set unread */
  1773. X    scan_all_roots = FALSE;
  1774. X    if( unread_selector ) {
  1775. X    page_char = '>';
  1776. X    end_char = 'Z';
  1777. X    page_root = 0;
  1778. X    last_root = -1;
  1779. X    cmd = 0;
  1780. X    } else {
  1781. X    page_char = page_select;
  1782. X    end_char = end_select;
  1783. X    page_root = select_page;
  1784. X    if( curr_p_art ) {
  1785. X        last_root = curr_p_art->root;
  1786. X    }
  1787. X    }
  1788. X    mask = unread_selector+1;
  1789. X
  1790. X    /* Leave empty roots selected for a short time to give them a chance
  1791. X    ** to 'q' out of the selector if they got here by mistake.
  1792. X    */
  1793. X    max_root = count_roots( FALSE );
  1794. X
  1795. X    /* If nothing to display, we're done. */
  1796. X    if( !article_count && !empty_ok ) {
  1797. X     all_empty:
  1798. X    clear_on_stop = FALSE;
  1799. X    mode = oldmode;
  1800. X    putchar( '\n' );
  1801. X    if( unread_selector ) {
  1802. X#ifdef VERBOSE
  1803. X        IF(verbose)
  1804. X        fputs( "\nNo articles to set unread.\n", stdout );
  1805. X        ELSE
  1806. X#endif
  1807. X#ifdef TERSE
  1808. X        fputs( "\nNo articles.\n", stdout ) FLUSH;
  1809. X#endif
  1810. X        unread_selector = 0;
  1811. X        mask = 1;
  1812. X    } else {
  1813. X#ifdef VERBOSE
  1814. X        IF(verbose)
  1815. X        fputs( "\nNo unread articles to select.\n", stdout );
  1816. X        ELSE
  1817. X#endif
  1818. X#ifdef TERSE
  1819. X        fputs( "\nNo articles.\n", stdout );    /* let "them" FLUSH */
  1820. X#endif
  1821. X    }
  1822. X    (void) count_roots( TRUE );
  1823. X    art = art_hold;
  1824. X    p_art = curr_p_art;
  1825. X    return 'q';
  1826. X    }
  1827. X    if( unread_selector ) {
  1828. X    for( j = 0; j < total.root; j++ ) {
  1829. X        selected_roots[j] |= 4;
  1830. X    }
  1831. X    }
  1832. X    if( page_root >= max_root ) {
  1833. X    ch = '<';
  1834. X    } else {
  1835. X    ch = '>';
  1836. X    }
  1837. X    cur_root = 0;
  1838. X    running_total = 0;
  1839. X    for( i = 0; i < page_root; i++ ) {
  1840. X    running_total += root_article_cnts[i];
  1841. X    }
  1842. X    do {
  1843. X    select_chars = getval( "SELECTCHARS", SELECTCHARS );
  1844. X    max_cnt = strlen( select_chars );
  1845. X    if( max_cnt > MAX_SEL ) {
  1846. X        max_cnt = MAX_SEL;
  1847. X    }
  1848. X    if( ch == '<' && i ) {
  1849. X        screen_line = 2;
  1850. X        cnt = 0;
  1851. X        /* Scan the roots in reverse to go back a page */
  1852. X        do {
  1853. X        if( !root_article_cnts[--i] ) {
  1854. X            continue;
  1855. X        }
  1856. X        first = root_subjects[i];
  1857. X        last = first + p_roots[i].subject_cnt;
  1858. X        line_cnt = 0;
  1859. X        for( j = first; j < last; j++ ) {
  1860. X            line_cnt += count_subj_lines( i, j );
  1861. X        }
  1862. X        if( line_cnt > LINES - 5 ) {
  1863. X            line_cnt = LINES - 5;
  1864. X        }
  1865. X        screen_line += line_cnt;
  1866. X        if( screen_line > LINES - 3 ) {
  1867. X            i++;
  1868. X            break;
  1869. X        }
  1870. X        running_total -= root_article_cnts[i];
  1871. X        cnt++;
  1872. X        } while( i > 0 && cnt < max_cnt );
  1873. X    }
  1874. X
  1875. X    /* Present a page of subjects to the user */
  1876. X#ifndef CLEAREOL
  1877. X    clear();
  1878. X#else
  1879. X    if( can_home_clear ) {
  1880. X        home_cursor();
  1881. X        maybe_eol();
  1882. X    } else {
  1883. X        clear();
  1884. X    }
  1885. X#endif
  1886. X    carriage_return();
  1887. X    page_root = i;
  1888. X    last_running = running_total;
  1889. X#ifdef NOFIREWORKS
  1890. X    no_sofire();
  1891. X#endif
  1892. X    standout();
  1893. X    fputs( ngname, stdout );
  1894. X    un_standout();
  1895. X    printf( "\t\t\t\t%ld %sarticle%s\n", (long)article_count,
  1896. X        unread_selector? "read " : nullstr,
  1897. X        article_count == 1 ? nullstr : "s" );
  1898. X#ifdef CLEAREOL
  1899. X    maybe_eol();
  1900. X#endif
  1901. X    putchar( '\n' ) FLUSH;
  1902. X    screen_line = 2;
  1903. X    for( cnt = 0; i < max_root && cnt < max_cnt; i++ ) {
  1904. X        if( last_root == i ) {
  1905. X        cur_root = cnt;
  1906. X        }
  1907. X        /* Check each root for articles to list */
  1908. X        if( !root_article_cnts[i] ) {
  1909. X        continue;
  1910. X        }
  1911. X        first = root_subjects[i];
  1912. X        last = first + p_roots[i].subject_cnt;
  1913. X
  1914. X        /* Compute how many lines we need to display the subjects/authors */
  1915. X        etc = FALSE;
  1916. X        line_cnt = 0;
  1917. X        for( j = first; j < last; j++ ) {
  1918. X        subj_line_cnt = count_subj_lines( i, j );
  1919. X        line_cnt += subj_line_cnt;
  1920. X        /* If this root is too long to fit on the screen all by
  1921. X        ** itself, trim it to fit and set the "etc" flag.
  1922. X        */
  1923. X        if( line_cnt > LINES - 5 ) {
  1924. X            last = j;
  1925. X            line_cnt -= subj_line_cnt;
  1926. X            if( line_cnt != LINES - 5 ) {
  1927. X            last++;
  1928. X            line_cnt = LINES - 5;
  1929. X            }
  1930. X            if( screen_line == 2 ) {
  1931. X            etc = TRUE;
  1932. X            }
  1933. X            break;
  1934. X        }
  1935. X        }
  1936. X        /* If it doesn't fit, save it for the next page */
  1937. X        if( screen_line + line_cnt > LINES - 3 ) {
  1938. X        break;
  1939. X        }
  1940. X        /* Output the subjects, with optional authors */
  1941. X        root_line[cnt] = screen_line;
  1942. X        running_total += root_article_cnts[i];
  1943. X        first_two_chars[0] = select_chars[cnt];
  1944. X        first_two_chars[1] = (selected_roots[i] & 4) ? '-' :
  1945. X                 (selected_roots[i] & mask) ? '+' : ' ';
  1946. X        author_line = screen_line;
  1947. X        for( j = first; j < last; j++ ) {
  1948. X        display_subj( i, j );
  1949. X        }
  1950. X        screen_line += line_cnt;
  1951. X        root_hold[cnt++] = i;
  1952. X        if( etc ) {
  1953. X        fputs( "      ...etc.", stdout );
  1954. X        i++;
  1955. X        break;
  1956. X        }
  1957. X    }/* for */
  1958. X    last_root = -1;
  1959. X    if( cur_root && cur_root >= cnt ) {
  1960. X        cur_root = cnt - 1;
  1961. X    }
  1962. X
  1963. X    /* Check if there is really anything left to display. */
  1964. X    if( !running_total && !empty_ok ) {
  1965. X        goto all_empty;
  1966. X    }
  1967. X    empty_ok = FALSE;
  1968. X
  1969. X    last_line = screen_line+1;
  1970. X#ifdef CLEAREOL
  1971. X    maybe_eol();
  1972. X#endif
  1973. X    putchar( '\n' ) FLUSH;
  1974. X    /* Prompt the user */
  1975. X    strcpy( promptbuf, "-- Select threads -- " );
  1976. X    if( i != max_root ) {
  1977. X        sprintf( promptbuf+21, "%s%ld%% [%c%c] --",
  1978. X        (!page_root? "Top " : nullstr),
  1979. X        (long)(running_total*100 / article_count),
  1980. X        page_char, end_char );
  1981. X    } else {
  1982. X        sprintf( promptbuf+21, "%s [%c%c] --",
  1983. X        (!page_root? "All" : "Bot"), end_char, page_char );
  1984. X    }
  1985. X    if( cur_root > cnt ) {
  1986. X        cur_root = 0;
  1987. X    }
  1988. X    screen_line = root_line[cur_root];
  1989. X#ifdef CLEAREOL
  1990. X    if( erase_screen && can_home_clear ) {
  1991. X        clear_rest();
  1992. X    }
  1993. X#endif
  1994. X    displayed_status = FALSE;
  1995. X      prompt_select:
  1996. X    standout();
  1997. X    fputs( promptbuf, stdout );
  1998. X    un_standout();
  1999. X    if( can_home ) {
  2000. X        carriage_return();
  2001. X        goto_line( last_line, screen_line );
  2002. X    }
  2003. X    got_dash = 0;
  2004. X    /* Grab some commands from the user */
  2005. X    for( ;; ) {
  2006. X        fflush(stdout);
  2007. X        eat_typeahead();
  2008. X#ifdef CONDSUB
  2009. X        getcmd( buf );
  2010. X        ch = *buf;
  2011. X#else
  2012. X        getcmd( tmpbuf );    /* If no conditionals, don't allow macros */ 
  2013. X        ch = *tmpbuf;
  2014. X        buf[0] = ch;
  2015. X        buf[1] = FINISHCMD;
  2016. X#endif
  2017. X        in_select = index( select_chars, ch );
  2018. X        /* Plaster any inherited empty roots on first command, if not q. */
  2019. X        if( cmd && (in_select || (ch != '\033' && ch != 'q')) ) {
  2020. X        max_root = count_roots( TRUE );
  2021. X        cmd = 0;
  2022. X        }
  2023. X        if( displayed_status && can_home ) {
  2024. X        goto_line( screen_line, last_line+1 );
  2025. X        erase_eol();
  2026. X        screen_line = last_line+1;
  2027. X        displayed_status = FALSE;
  2028. X        }
  2029. X        if( ch == '-' ) {
  2030. X        got_dash = 1;
  2031. X        if( !can_home ) {
  2032. X            putchar( '-' );
  2033. X            fflush( stdout );
  2034. X        }
  2035. X        continue;
  2036. X        }
  2037. X        if( ch == ' ' ) {
  2038. X        if( i == max_root ) {
  2039. X            ch = end_char;
  2040. X        } else {
  2041. X            ch = page_char;
  2042. X        }
  2043. X        }
  2044. X        if( !in_select && (index( "<>^$!?&:/hDJLNqQUXZ\n\r\t\033", ch )
  2045. X         || ch == Ctl('l') || ch == Ctl('r') || ch == Ctl('k')) ) {
  2046. X        break;
  2047. X        }
  2048. X        if( in_select ) {
  2049. X        j = in_select - select_chars;
  2050. X        if( j >= cnt ) {
  2051. X            dingaling();
  2052. X            j = -1;
  2053. X        } else if( got_dash ) {
  2054. X            ;
  2055. X        } else if( selected_roots[root_hold[j]] & mask ) {
  2056. X            action = (unread_selector ? 'k' : '-');
  2057. X        } else {
  2058. X            action = '+';
  2059. X        }
  2060. X        } else if( ch == 'y' || ch == '.' ) {
  2061. X        j = cur_root;
  2062. X        if( selected_roots[root_hold[j]] & mask ) {
  2063. X            action = (unread_selector ? 'k' : '-');
  2064. X        } else {
  2065. X            action = '+';
  2066. X        }
  2067. X        } else if( ch == 'k' || ch == 'j' || ch == ',' ) {
  2068. X        j = cur_root;
  2069. X        action = 'k';
  2070. X        } else if( ch == 'm' || ch == '\\' ) {
  2071. X        j = cur_root;
  2072. X        action = 'm';
  2073. X        } else if( ch == '@' ) {
  2074. X        cur_root = 0;
  2075. X        j = cnt-1;
  2076. X        got_dash = 1;
  2077. X        action = '@';
  2078. X        } else if( ch == '[' || ch == 'p' ) {
  2079. X        if( --cur_root < 0 ) {
  2080. X            cur_root = cnt ? cnt-1 : 0;
  2081. X        }
  2082. X        j = -1;
  2083. X        } else if( ch == ']' || ch == 'n' ) {
  2084. X        if( ++cur_root >= cnt ) {
  2085. X            cur_root = 0;
  2086. X        }
  2087. X        j = -1;
  2088. X        } else {
  2089. X        if( can_home ) {
  2090. X            goto_line( screen_line, last_line+1 );
  2091. X            screen_line = last_line+1;
  2092. X        } else {
  2093. X            putchar( '\n' );
  2094. X        }
  2095. X        printf( "Type ? for help." );
  2096. X        settle_down();
  2097. X        displayed_status = TRUE;
  2098. X
  2099. X        if( can_home ) {
  2100. X            carriage_return();
  2101. X        } else {
  2102. X            putchar( '\n' );
  2103. X        }
  2104. X        j = -1;
  2105. X        }
  2106. X        if( j >= 0 ) {
  2107. X        if( !got_dash ) {
  2108. X            cur_root = j;
  2109. X        } else {
  2110. X            got_dash = 0;
  2111. X            if( j < cur_root ) {
  2112. X            ch = cur_root-1;
  2113. X            cur_root = j;
  2114. X            j = ch;
  2115. X            }
  2116. X        }
  2117. X        if( ++j == cnt ) {
  2118. X            j = 0;
  2119. X        }
  2120. X        do {
  2121. X          register int r;
  2122. X          register char maskr = mask;
  2123. X            r = root_hold[cur_root];
  2124. X            if( can_home ) {
  2125. X            goto_line( screen_line, root_line[cur_root] );
  2126. X            screen_line = root_line[cur_root];
  2127. X            }
  2128. X            putchar( select_chars[cur_root] );
  2129. X            if( action == '@' ) {
  2130. X            if( selected_roots[r] & 4 ) {
  2131. X                ch = (unread_selector ? '+' : ' ');
  2132. X            } else if( unread_selector ) {
  2133. X                ch = 'k';
  2134. X            } else
  2135. X            if( selected_roots[r] & maskr ) {
  2136. X                ch = '-';
  2137. X            } else {
  2138. X                ch = '+';
  2139. X            }
  2140. X            } else {
  2141. X            ch = action;
  2142. X            }
  2143. X            switch( ch ) {
  2144. X            case '+':
  2145. X            if( !(selected_roots[r] & maskr) ) {
  2146. X                selected_roots[r] |= maskr;
  2147. X                selected_root_cnt++;
  2148. X                selected_count += root_article_cnts[r];
  2149. X                putchar( '+' );
  2150. X            }
  2151. X            /* FALL THROUGH */
  2152. X            case 'm':
  2153. X            if( selected_roots[r] & 4 ) {
  2154. X                selected_roots[r] &= ~4;
  2155. X                if( ch == 'm' ) {
  2156. X                putchar( ' ' );
  2157. X                }
  2158. X            } else if( ch == 'm' ) {
  2159. X                goto unsel;
  2160. X            }
  2161. X            break;
  2162. X            case 'k':
  2163. X            if( !(selected_roots[r] & 4) ) {
  2164. X                selected_roots[r] |= 4;
  2165. X                putchar( '-' );
  2166. X            }
  2167. X            /* FALL THROUGH */
  2168. X            case '-':
  2169. X            unsel:
  2170. X            if( selected_roots[r] & maskr ) {
  2171. X                selected_roots[r] &= ~maskr;
  2172. X                selected_root_cnt--;
  2173. X                selected_count -= root_article_cnts[r];
  2174. X                if( ch != 'k' ) {
  2175. X                putchar( ' ' );
  2176. X                }
  2177. X            }
  2178. X            break;
  2179. X            }
  2180. X            fflush( stdout );
  2181. X            if( ++cur_root == cnt ) {
  2182. X            cur_root = 0;
  2183. X            }
  2184. X            if( can_home ) {
  2185. X            carriage_return();
  2186. X            }
  2187. X        } while( cur_root != j );
  2188. X        } else {
  2189. X        got_dash = FALSE;
  2190. X        }
  2191. X        if( can_home ) {
  2192. X        goto_line( screen_line, root_line[cur_root] );
  2193. X        screen_line = root_line[cur_root];
  2194. X        }
  2195. X    }/* for */
  2196. X    if( can_home) {
  2197. X        goto_line( screen_line, last_line );
  2198. X    }
  2199. X    clean_screen = TRUE;
  2200. X      do_command:
  2201. X    if( ch == 'L' ) {
  2202. X        if( !*++display_mode ) {
  2203. X        display_mode = select_order;
  2204. X        }
  2205. X        ch = Ctl('l');
  2206. X        cur_root = 0;
  2207. X    } else if( ch == '$' ) {
  2208. X        ch = '<';
  2209. X        page_root = max_root;
  2210. X        last_running = article_count;
  2211. X        cur_root = 0;
  2212. X    } else if( ch == '^' || ch == Ctl('r') ) {
  2213. X        ch = '>';
  2214. X        i = 0;
  2215. X        running_total = 0;
  2216. X        cur_root = 0;
  2217. X    } else if( ch == 'h' || ch == '?' ) {
  2218. X        putchar( '\n' );
  2219. X        if( (ch = help_select()) || (ch = pause_getcmd()) ) {
  2220. X        goto got_cmd;
  2221. X        }
  2222. X        ch = Ctl('l');
  2223. X    } else if( index( ":/&!", ch ) ) {
  2224. X        erase_eol();        /* erase the prompt */
  2225. X        if( !finish_command( TRUE ) ) {    /* get rest of command */
  2226. X        if( clean_screen ) {
  2227. X            screen_line = root_line[cur_root];
  2228. X            goto prompt_select;
  2229. X        }
  2230. X        goto extend_done;
  2231. X        }
  2232. X        if( ch == '&' || ch == '!' ) {
  2233. X        one_command = TRUE;
  2234. X        perform( buf, FALSE );
  2235. X        one_command = FALSE;
  2236. X
  2237. X        putchar( '\n' ) FLUSH;
  2238. X        clean_screen = FALSE;
  2239. X        } else {
  2240. X        int selected_save = selected_root_cnt;
  2241. X
  2242. X        if( ch == ':' ) {
  2243. X            clean_screen = (use_selected() == 2) && clean_screen;
  2244. X        } else {
  2245. X            /* Force the search to begin at absfirst or firstart,
  2246. X            ** depending upon whether they specified the 'r' option.
  2247. X            */
  2248. X            art = lastart+1;
  2249. X            page_line = 1;
  2250. X            switch( art_search( buf, sizeof buf, FALSE ) ) {
  2251. X            case SRCH_ERROR:
  2252. X            case SRCH_ABORT:
  2253. X            case SRCH_INTR:
  2254. X            fputs( "\nInterrupted\n", stdout ) FLUSH;
  2255. X            break;
  2256. X            case SRCH_DONE:
  2257. X            case SRCH_SUBJDONE:
  2258. X            fputs( "Done\n", stdout ) FLUSH;
  2259. X            break;
  2260. X            case SRCH_NOTFOUND:
  2261. X            fputs( "\nNot found.\n", stdout ) FLUSH;
  2262. X            break;
  2263. X            case SRCH_FOUND:
  2264. X            break;
  2265. X            }
  2266. X            clean_screen = FALSE;
  2267. X        }
  2268. X        /* Recount, in case something has changed. */
  2269. X        max_root = count_roots( !unread_selector );
  2270. X
  2271. X        if( (selected_save -= selected_root_cnt) != 0 ) {
  2272. X            putchar( '\n' );
  2273. X            if( selected_save < 0 ) {
  2274. X            fputs( "S", stdout );
  2275. X            selected_save *= -1;
  2276. X            } else {
  2277. X            fputs( "Des", stdout );
  2278. X            }
  2279. X            printf( "elected %d thread%s.", selected_save,
  2280. X            selected_save == 1 ? nullstr : "s" );
  2281. X            clean_screen = FALSE;
  2282. X        }
  2283. X        if( !clean_screen ) {
  2284. X            putchar('\n') FLUSH;
  2285. X        }
  2286. X        }/* if !& or :/ */
  2287. X
  2288. X        if( clean_screen ) {
  2289. X        carriage_return();
  2290. X        up_line();
  2291. X        erase_eol();
  2292. X        screen_line = root_line[cur_root];
  2293. X        goto prompt_select;
  2294. X        }
  2295. X      extend_done:
  2296. X        if( (ch = pause_getcmd()) ) {
  2297. X          got_cmd:
  2298. X        if( ch > 0 ) {
  2299. X            /* try to optimize the screen update for some commands. */
  2300. X            if( !index( select_chars, ch )
  2301. X             && (index( "<>^$!?&:/hDJLNqQUXZ\n\r\t\033", ch )
  2302. X              || ch == Ctl('k')) ) {
  2303. X            buf[0] = ch;
  2304. X            buf[1] = FINISHCMD;
  2305. X            goto do_command;
  2306. X            }
  2307. X            pushchar( ch | 0200 );
  2308. X        }
  2309. X        }
  2310. X        ch = Ctl('l');
  2311. X    } else if( ch == Ctl('k') ) {
  2312. X        edit_kfile();
  2313. X        ch = Ctl('l');
  2314. X    } else if( !unread_selector && (ch == 'X' || ch == 'D' || ch == 'J') ) {
  2315. X        if( ch == 'D' ) {
  2316. X        j = page_root;
  2317. X        last = i;
  2318. X        } else {
  2319. X        j = 0;
  2320. X        last = max_root;
  2321. X        }
  2322. X        for( ; j < last; j++ ) {
  2323. X        if( (!(selected_roots[j] & 1) ^ (ch == 'J'))
  2324. X         && (cnt = root_article_cnts[j]) ) {
  2325. X            p_art = p_articles + p_roots[j].articles;
  2326. X            art = 0;
  2327. X            follow_thread( 'J' );
  2328. X        }
  2329. X        }
  2330. X        max_root = count_roots( TRUE );
  2331. X        if( article_count
  2332. X         && (ch == 'J' || (ch == 'D' && !selected_root_cnt)) ) {
  2333. X        ch = Ctl('l');
  2334. X        cur_root = 0;
  2335. X        } else {
  2336. X        break;
  2337. X        }
  2338. X    } else if( ch == 'J' ) {
  2339. X        for( j = 0; j < max_root; j++ ) {
  2340. X        selected_roots[j] = (selected_roots[j] & ~2) | 4;
  2341. X        }
  2342. X        selected_root_cnt = selected_count = 0;
  2343. X        ch = Ctl('l');
  2344. X    }
  2345. X    if( ch == '>' ) {
  2346. X        cur_root = 0;
  2347. X    } else if( ch == '<' || (page_root && page_root >= max_root) ) {
  2348. X        cur_root = 0;
  2349. X        running_total = last_running;
  2350. X        if( !(i = page_root) || !max_root ) {
  2351. X        ch = '>';
  2352. X        } else {
  2353. X        ch = '<';
  2354. X        }
  2355. X    } else if( ch == Ctl('l') ) {
  2356. X        i = page_root;
  2357. X        running_total = last_running;
  2358. X        ch = '>';
  2359. X    } else if( ch == '\r' || ch == '\n' ) {
  2360. X        if( !selected_root_cnt ) {
  2361. X          register r = root_hold[cur_root];
  2362. X        selected_roots[r] = mask;
  2363. X        selected_root_cnt++;
  2364. X        selected_count += root_article_cnts[r];
  2365. X        }
  2366. X    }
  2367. X    } while( (ch == '>' && i < max_root) || ch == '<' );
  2368. X    putchar( '\n' ) FLUSH;
  2369. X
  2370. X    if( unread_selector ) {
  2371. X    /* Turn selections into unread selected roots.  Let count_roots()
  2372. X    ** fix the counts after we're through.
  2373. X    */
  2374. X    last_root = -1;
  2375. X    for( j = 0; j < total.root; j++ ) {
  2376. X        if( !(selected_roots[j] & 4) ) {
  2377. X        if( selected_roots[j] & 2 ) {
  2378. X            selected_roots[j] = 1;
  2379. X        }
  2380. X        p_art = p_articles + p_roots[j].articles;
  2381. X        art = 0;
  2382. X        follow_thread( 'u' );
  2383. X        } else {
  2384. X        selected_roots[j] &= ~4;
  2385. X        }
  2386. X    }
  2387. X    } else {
  2388. X    select_page = page_root;
  2389. X    for( j = 0; j < total.root; j++ ) {
  2390. X        if( selected_roots[j] & 4 ) {
  2391. X        selected_roots[j] = 0;
  2392. X        p_art = p_articles + p_roots[j].articles;
  2393. X        art = 0;
  2394. X        follow_thread( 'J' );
  2395. X        }
  2396. X    }
  2397. X    }
  2398. X    if( ch == 'U' ) {
  2399. X    unread_selector = !unread_selector;
  2400. X    empty_ok = TRUE;
  2401. X    goto select_threads;
  2402. X    }
  2403. X
  2404. X    if( unread_selector ) {
  2405. X    unread_selector = 0;
  2406. X    mask = 1;
  2407. X    (void) count_roots( FALSE );
  2408. X    }
  2409. X    if( ch == '\033' || Ctl(ch) == Ctl('q') ) {
  2410. X    ch = (ch == 'Q' ? 'Q' : 'q');
  2411. X    art = art_hold;
  2412. X    p_art = curr_p_art;
  2413. X    } else if( ch == 'N' ) {
  2414. X    art = art_hold;
  2415. X    p_art = curr_p_art;
  2416. X    } else {
  2417. X    first_art();
  2418. X    }
  2419. X    clear_on_stop = FALSE;
  2420. X    mode = oldmode;
  2421. X    return ch;
  2422. X}
  2423. X
  2424. Xstatic int author_cnt, first_author;
  2425. X
  2426. X/* Counts the number of lines needed to output a subject, including optional
  2427. X** authors.
  2428. X*/
  2429. Xstatic int
  2430. Xcount_subj_lines( root, subj )
  2431. Xint root;
  2432. Xint subj;
  2433. X{
  2434. X    PACKED_ARTICLE *artp, *root_limit;
  2435. X    int author_subj;
  2436. X
  2437. X    author_cnt = 0;
  2438. X    author_subj = subj;
  2439. X    first_author = -1;
  2440. X
  2441. X    if( !subject_cnts[subj] ) {
  2442. X    return 0;
  2443. X    }
  2444. X    if( *display_mode == 's' ) {    /* no-author mode takes one line */
  2445. X    return ++author_cnt;
  2446. X    }
  2447. X    bzero( author_cnts, total.author * sizeof (WORD) );
  2448. X
  2449. X    /* Count authors associated with this subject.  Increments author_cnts. */
  2450. X    artp = p_articles + p_roots[root].articles;
  2451. X    root_limit = upper_limit( artp, FALSE );
  2452. X    for( ; artp != root_limit; artp++ ) {
  2453. X    if( artp->subject == author_subj
  2454. X     && (!was_read( artp->num ) ^ unread_selector) ) {
  2455. X        if( artp->author < 0 || artp->author >= total.author ) {
  2456. X        printf( "\
  2457. XFound invalid author (%d) with valid subject (%d)! [%ld]\n",
  2458. X            artp->author, artp->subject, artp->num );
  2459. X        artp->author = 0;
  2460. X        } else {
  2461. X        if( first_author < 0 ) {
  2462. X            first_author = artp->author;
  2463. X        }
  2464. X        if( !author_cnts[artp->author]++ ) {
  2465. X            author_cnt++;
  2466. X        }
  2467. X        }
  2468. X    }
  2469. X    }
  2470. X
  2471. X    if( *display_mode == 'm' ) {
  2472. X    return (author_cnt+4)/3;
  2473. X    } else {
  2474. X    return author_cnt;
  2475. X    }
  2476. X}
  2477. X
  2478. Xstatic void
  2479. Xdisplay_subj( root, subj )
  2480. Xint root;
  2481. Xint subj;
  2482. X{
  2483. X    PACKED_ARTICLE *artp, *root_limit;
  2484. X    char *str;
  2485. X
  2486. X    count_subj_lines( root, subj );
  2487. X    if( !author_cnt ) {
  2488. X    return;
  2489. X    }
  2490. X    artp = p_articles + p_roots[root].articles;
  2491. X    if( artp->subject != -1 && (artp->flags & ROOT_ARTICLE)
  2492. X     && (!was_read(artp->num) ^ unread_selector) ) {
  2493. X    str = nullstr;
  2494. X    } else {
  2495. X    str = ">";
  2496. X    }
  2497. X#ifdef CLEAREOL
  2498. X    maybe_eol();
  2499. X#endif
  2500. X    if( *display_mode == 's' ) {
  2501. X    printf( "%s%3d  %s%.71s\n", first_two_chars,
  2502. X        subject_cnts[subj], str, subject_ptrs[subj] ) FLUSH;
  2503. X    } else {
  2504. X    printf( "%s%-16.16s%3d  %s%.55s", first_two_chars,
  2505. X        author_ptrs[first_author],
  2506. X        subject_cnts[subj], str, subject_ptrs[subj] );
  2507. X    if( author_cnt > 1 ) {
  2508. X        author_cnts[first_author] = 0;
  2509. X        author_cnt = 0;
  2510. X        root_limit = upper_limit( artp, FALSE );
  2511. X        for( ; artp != root_limit; artp++ ) {
  2512. X        if( artp->author >= 0 && author_cnts[artp->author] ) {
  2513. X            switch( author_cnt % 3 ) {
  2514. X            case 0:
  2515. X            putchar( '\n' ) FLUSH;
  2516. X            if( ++author_line >= LINES - 3 ) {
  2517. X                return;
  2518. X            }
  2519. X#ifdef CLEAREOL
  2520. X            maybe_eol();
  2521. X#endif
  2522. X            putchar( ' ' );
  2523. X            putchar( ' ' );
  2524. X            break;
  2525. X            case 1:
  2526. X            putchar( '\t' );
  2527. X            putchar( '\t' );
  2528. X            break;
  2529. X            case 2:
  2530. X            putchar( '\t' );
  2531. X            break;
  2532. X            }
  2533. X            author_cnt += (*display_mode == 'm');
  2534. X            printf( "%-16.16s", author_ptrs[artp->author] );
  2535. X            author_cnts[artp->author] = 0;
  2536. X        }/* if */
  2537. X        }/* for */
  2538. X    }/* if */
  2539. X    putchar( '\n' ) FLUSH;
  2540. X    author_line++;
  2541. X    }/* if */
  2542. X    first_two_chars[0] = first_two_chars[1] = ' ';
  2543. X}
  2544. X
  2545. X/* Get each root's article count, and subject count(s); count total
  2546. X** articles and selected articles (use unread_selector to determine
  2547. X** whether to count read or unread articles); deselect any roots we
  2548. X** find that are empty (if do_unselect is TRUE); find the last non-
  2549. X** empty root, and return its count (the index+1).
  2550. X*/
  2551. Xint
  2552. Xcount_roots( do_unselect )
  2553. Xbool do_unselect;
  2554. X{
  2555. X    register int count;
  2556. X    register PACKED_ARTICLE *artp, *root_limit, *art_limit;
  2557. X    int last_root = -1;
  2558. X
  2559. X    article_count = selected_count = selected_root_cnt = 0;
  2560. X
  2561. X    if( !(artp = p_articles) ) {
  2562. X    return 0;
  2563. X    }
  2564. X    art_limit = artp + total.article;
  2565. X    root_limit = upper_limit( artp, 0 );
  2566. X
  2567. X    bzero( subject_cnts, total.subject * sizeof (WORD) );
  2568. X    count = 0;
  2569. X
  2570. X    for( ;; ) {
  2571. X    if( artp->subject == -1 ) {
  2572. X        if( !was_read( artp->num ) ) {
  2573. X        oneless( artp->num );
  2574. X        }
  2575. X    } else if( (!was_read( artp->num ) ^ unread_selector) ) {
  2576. X        count++;
  2577. X        subject_cnts[artp->subject]++;
  2578. X    }
  2579. X    if( ++artp == root_limit ) {
  2580. X        register int root_num = artp[-1].root;
  2581. X        register char maskr = mask;
  2582. X
  2583. X        root_article_cnts[root_num] = count;
  2584. X        if( count ) {
  2585. X        article_count += count;
  2586. X        if( selected_roots[root_num] & maskr ) {
  2587. X            selected_roots[root_num] &= ~4;
  2588. X            selected_root_cnt++;
  2589. X            selected_count += count;
  2590. X        }
  2591. X        last_root = root_num;
  2592. X        } else if( do_unselect ) {
  2593. X        selected_roots[root_num] &= ~maskr;
  2594. X        } else if( selected_roots[root_num] & maskr ) {
  2595. X        selected_roots[root_num] &= ~4;
  2596. X        selected_root_cnt++;
  2597. X        }
  2598. X        if( artp == art_limit ) {
  2599. X        break;
  2600. X        }
  2601. X        root_limit = upper_limit( artp, 0 );
  2602. X        count = 0;
  2603. X    }
  2604. X    }
  2605. X    if( do_unselect ) {
  2606. X    scan_all_roots = !article_count;
  2607. X    }
  2608. X    unthreaded = toread[ng] - article_count;
  2609. X
  2610. X    return last_root+1;
  2611. X}
  2612. X
  2613. X/* Count the unread articles attached to the given root number.
  2614. X*/
  2615. Xint
  2616. Xcount_one_root( root_num )
  2617. Xint root_num;
  2618. X{
  2619. X    int last = (root_num == total.root-1 ? total.article
  2620. X                     : p_roots[root_num+1].articles);
  2621. X    register int count = 0, i;
  2622. X
  2623. X    for( i = p_roots[root_num].articles; i < last; i++ ) {
  2624. X    if( p_articles[i].subject != -1 && !was_read( p_articles[i].num ) ) {
  2625. X        count++;
  2626. X    }
  2627. X    }
  2628. X    root_article_cnts[root_num] = count;
  2629. X
  2630. X    return count;
  2631. X}
  2632. X
  2633. X#endif /* USETHREADS */
  2634. END_OF_FILE
  2635.   if test 22344 -ne `wc -c <'rt-select.c'`; then
  2636.     echo shar: \"'rt-select.c'\" unpacked with wrong size!
  2637.   fi
  2638.   # end of 'rt-select.c'
  2639. fi
  2640. if test -f 'uudecode.h' -a "${1}" != "-c" ; then 
  2641.   echo shar: Will not clobber existing file \"'uudecode.h'\"
  2642. else
  2643.   echo shar: Extracting \"'uudecode.h'\" \(404 characters\)
  2644.   sed "s/^X//" >'uudecode.h' <<'END_OF_FILE'
  2645. X/* $Header: uudecode.h,v 4.3.3.2 91/01/16 03:41:53 davison Trn $
  2646. X**
  2647. X** $Log:    uudecode.h,v $
  2648. X** Revision 4.3.3.2  91/01/16  03:41:53  davison
  2649. X** Added optional prototyping.
  2650. X** 
  2651. X** Revision 4.3.3.1  90/06/20  22:49:08  davison
  2652. X** Initial Trn Release
  2653. X** 
  2654. X*/
  2655. X
  2656. XEXT FILE *uu_out INIT(NULL);
  2657. XEXT char uu_fname[MAXFILENAME];
  2658. X
  2659. Xint uud_start ANSI((char *));
  2660. Xint uud_send ANSI((void));
  2661. Xint uudecode ANSI((FILE *));
  2662. END_OF_FILE
  2663.   if test 404 -ne `wc -c <'uudecode.h'`; then
  2664.     echo shar: \"'uudecode.h'\" unpacked with wrong size!
  2665.   fi
  2666.   # end of 'uudecode.h'
  2667. fi
  2668. echo shar: End of archive 8 \(of 14\).
  2669. cp /dev/null ark8isdone
  2670. MISSING=""
  2671. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ; do
  2672.     if test ! -f ark${I}isdone ; then
  2673.     MISSING="${MISSING} ${I}"
  2674.     fi
  2675. done
  2676. if test "${MISSING}" = "" ; then
  2677.     echo You have unpacked all 14 archives.
  2678.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  2679. else
  2680.     echo You still must unpack the following archives:
  2681.     echo "        " ${MISSING}
  2682. fi
  2683. exit 0
  2684.